From 04a1177485954892560854ca2baad0c372cfa752 Mon Sep 17 00:00:00 2001
From: 10000ge10000 <10000ge10000@users.noreply.github.com>
Date: Fri, 13 Mar 2026 14:07:53 +0800
Subject: [PATCH] chore: remove offline installation support completely
---
.github/workflows/build.yml | 39 ---
.gitignore | 1 -
CHANGELOG.md | 2 -
README.md | 8 -
luasrc/controller/openclaw.lua | 10 -
luasrc/view/openclaw/status.htm | 14 -
root/usr/bin/openclaw-env | 106 ------
scripts/build_offline_run.sh | 594 --------------------------------
scripts/download_deps.sh | 304 ----------------
scripts/gen-release-body.sh | 7 -
scripts/upload_openlist.sh | 238 ++-----------
11 files changed, 33 insertions(+), 1290 deletions(-)
delete mode 100755 scripts/build_offline_run.sh
delete mode 100755 scripts/download_deps.sh
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 0ff245a..3a104a8 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -7,9 +7,6 @@ on:
description: '版本号 (留空则读取 VERSION 文件)'
required: false
default: ''
- build_offline:
- description: '是否构建离线安装包 (~130MB/架构)'
- required: false
type: boolean
default: false
create_release:
@@ -78,31 +75,9 @@ jobs:
chmod +x scripts/build_ipk.sh
sh scripts/build_ipk.sh dist
- # ── 离线安装包 (可选) ──
- - name: Setup Node.js (for offline build)
- if: github.event.inputs.build_offline == 'true'
- uses: actions/setup-node@v4
- with:
- node-version: '22'
-
- - name: Download offline dependencies
- if: github.event.inputs.build_offline == 'true'
- run: |
- chmod +x scripts/download_deps.sh
- sh scripts/download_deps.sh .offline-cache
- env:
- NODE_VERSION: ${{ steps.build_versions.outputs.node_version }}
OC_VERSION: ${{ steps.build_versions.outputs.oc_version }}
- - name: Build offline .run (musl only)
- if: github.event.inputs.build_offline == 'true'
- run: |
- chmod +x scripts/build_offline_run.sh
- sh scripts/build_offline_run.sh dist/
- env:
- CACHE_DIR: .offline-cache
- NODE_VERSION: ${{ steps.build_versions.outputs.node_version }}
# ── 通用步骤 ──
@@ -148,16 +123,6 @@ jobs:
OPENLIST_PATH: ${{ secrets.OPENLIST_PATH }}
UPLOAD_MODE: online
- - name: Upload to OpenList (offline)
- if: github.event.inputs.upload_openlist == 'true' && github.event.inputs.build_offline == 'true'
- run: |
- sh scripts/upload_openlist.sh dist/
- env:
- OPENLIST_URL: ${{ secrets.OPENLIST_URL }}
- OPENLIST_USER: ${{ secrets.OPENLIST_USER }}
- OPENLIST_PASS: ${{ secrets.OPENLIST_PASS }}
- OPENLIST_PATH: ${{ secrets.OPENLIST_PATH }}
- UPLOAD_MODE: offline
- name: Create Release
if: github.event.inputs.create_release == 'true'
@@ -182,11 +147,7 @@ jobs:
opkg install luci-app-openclaw_${{ steps.version.outputs.version }}-1_all.ipk
```
- **离线安装** (无需联网,包含全部依赖)
```bash
- # 将对应架构的 *_offline.run 传到路由器
- scp luci-app-openclaw_*_offline.run root@路由器IP:/tmp/
- ssh root@路由器IP "sh /tmp/luci-app-openclaw_*_offline.run"
```
[使用文档](https://github.com/10000ge10000/luci-app-openclaw#readme) · [问题反馈](https://github.com/10000ge10000/luci-app-openclaw/issues) · [B站](https://space.bilibili.com/59438380) · [博客](https://blog.910501.xyz/)
diff --git a/.gitignore b/.gitignore
index 2edddfe..60436f5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,7 +8,6 @@ dist/
*.run
# Offline bundle cache (generated by scripts/download_deps.sh)
-.offline-cache/
# Runtime data (contains sensitive API keys / tokens)
opt/
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a0adbc6..ff38197 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -17,8 +17,6 @@
- **离线 .run 安装包**: 构建包含 Node.js + OpenClaw + LuCI 插件的全合一自解压包,用户**无需联网**即可完成安装
- **musl 架构支持**: 离线包支持 x86_64-musl、aarch64-musl 两种架构 (OpenWrt/iStoreOS 均使用 musl)
- **依赖预下载脚本** (`scripts/download_deps.sh`): 在构建机上预下载所有离线依赖
-- **离线构建脚本** (`scripts/build_offline_run.sh`): 将预下载的依赖打包为各架构的离线 .run
-- **GitHub Actions CI** (`.github/workflows/build-offline.yml`): 离线包自动构建 + 发布
- **node_modules 精简**: 自动删除文档、测试、TypeScript 源码等非必要文件,减小 30%+ 体积
- **磁盘空间预检查**: 安装前检测可用空间是否满足 500MB 最低要求
- **架构/libc 自动检测**: 安装时自动校验当前设备是否匹配安装包架构
diff --git a/README.md b/README.md
index a6e293c..5e5ab45 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,6 @@
[](https://space.bilibili.com/59438380)
[](https://blog.910501.xyz/)
[](https://github.com/10000ge10000/luci-app-openclaw/actions/workflows/build.yml)
-[](https://github.com/10000ge10000/luci-app-openclaw/actions/workflows/build-offline.yml)
[](LICENSE)
[OpenClaw](https://github.com/nicepkg/openclaw) AI 网关的 OpenWrt LuCI 管理插件。
@@ -96,21 +95,16 @@ rm -f /tmp/luci-indexcache /tmp/luci-modulecache/*
**下载离线包**(在联网的电脑上):
-前往 [Releases](https://github.com/10000ge10000/luci-app-openclaw/releases) 页面下载对应架构的 `_offline.run` 文件:
| 架构 | 文件名 |
|------|--------|
-| x86_64 | `luci-app-openclaw_*_x86_64-musl_offline.run` |
-| aarch64 (ARM64) | `luci-app-openclaw_*_aarch64-musl_offline.run` |
**传输到路由器并安装**:
```bash
# 从电脑传输到路由器(替换为实际文件名和路由器 IP)
-scp luci-app-openclaw_*_offline.run root@192.168.1.1:/tmp/
# SSH 登录路由器后执行安装
-sh /tmp/luci-app-openclaw_*_offline.run
```
> **提示**:离线包约 130MB,ARM 设备上安装需要 3-5 分钟(主要是解压时间)。
@@ -146,13 +140,11 @@ luci-app-openclaw/
├── scripts/
│ ├── build_ipk.sh # 本地 IPK 构建
│ ├── build_run.sh # .run 安装包构建
-│ ├── build_offline_run.sh # 离线 .run 安装包构建
│ ├── download_deps.sh # 下载离线依赖 (Node.js + OpenClaw)
│ ├── upload_openlist.sh # 上传到网盘 (OpenList)
│ └── build-node-musl.sh # 编译 Node.js musl 静态链接版本
└── .github/workflows/
├── build.yml # 在线构建 + 发布
- ├── build-offline.yml # 离线包构建 + 发布
└── build-node-musl.yml # Node.js musl 构建
```
diff --git a/luasrc/controller/openclaw.lua b/luasrc/controller/openclaw.lua
index ad38cfd..5ba6c07 100644
--- a/luasrc/controller/openclaw.lua
+++ b/luasrc/controller/openclaw.lua
@@ -82,16 +82,6 @@ function action_status()
end
-- 安装方式检测 (离线 / 在线)
- local olf = io.open("/usr/share/openclaw/.offline-install", "r")
- if olf then
- local content = olf:read("*a")
- olf:close()
- result.install_type = "offline"
- result.install_date = content:match("date=([^\n]+)") or ""
- result.install_arch = content:match("arch=([^\n]+)") or ""
- else
- result.install_type = "online"
- end
-- 检查 Node.js
local node_bin = "/opt/openclaw/node/bin/node"
diff --git a/luasrc/view/openclaw/status.htm b/luasrc/view/openclaw/status.htm
index 3132909..b2a8ebe 100644
--- a/luasrc/view/openclaw/status.htm
+++ b/luasrc/view/openclaw/status.htm
@@ -57,8 +57,6 @@
.oc-badge-starting { background: #fff8c5; color: #9a6700; }
.oc-badge-disabled { background: #f0f0f0; color: #656d76; }
.oc-badge-unknown { background: #fff8c5; color: #9a6700; }
-.oc-badge-offline { background: #ddf4ff; color: #0969da; border: 1px solid #54aeff; }
-.oc-badge-online { background: #f0f0f0; color: #656d76; }
.oc-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; margin-right: 6px; vertical-align: middle; }
.oc-dot-green { background: #1a7f37; }
.oc-dot-red { background: #cf222e; }
@@ -80,7 +78,6 @@
| Node.js | - |
| OpenClaw | - |
| 插件版本 | - |
- | 安装方式 | - |
@@ -151,17 +148,6 @@
document.getElementById('oc-st-oc-ver').textContent = d.oc_version ? ('v' + d.oc_version) : '未安装';
document.getElementById('oc-st-plugin').textContent = d.plugin_version ? ('v' + d.plugin_version) : '-';
- var itEl = document.getElementById('oc-st-install-type');
- if (d.install_type === 'offline') {
- var tip = '📦 离线安装';
- if (d.install_arch) tip += ' (' + d.install_arch + ')';
- if (d.install_date) tip += '\n安装时间: ' + d.install_date;
- itEl.innerHTML = '📦 离线版';
- } else {
- itEl.innerHTML = '🌐 在线版';
- }
-
- } catch(e) {
document.getElementById('oc-st-status').innerHTML = '查询失败';
}
});
diff --git a/root/usr/bin/openclaw-env b/root/usr/bin/openclaw-env
index 1d1574a..4e0b856 100755
--- a/root/usr/bin/openclaw-env
+++ b/root/usr/bin/openclaw-env
@@ -624,112 +624,8 @@ do_factory_reset() {
}
# ── 离线安装 (从本地文件安装 Node.js + OpenClaw) ──
-do_setup_offline() {
- local offline_dir="${2:-/tmp/openclaw-offline}"
-
- if [ ! -d "$offline_dir" ]; then
- log_error "离线安装目录不存在: $offline_dir"
- log_error "此命令通常由离线 .run 安装器自动调用"
- exit 1
- fi
-
- # 检查是否已安装
- if [ -x "$NODE_BIN" ] && [ -n "$(find_oc_entry)" ]; then
- log_warn "OpenClaw 运行环境已安装"
- log_warn "如需重新离线安装,请先卸载现有环境"
- exit 0
- fi
-
- echo "╔══════════════════════════════════════════════════════════════╗"
- echo "║ 一万AI分享 OpenClaw 离线安装 ║"
- echo "╚══════════════════════════════════════════════════════════════╝"
- echo ""
- echo " 架构: $(uname -m)"
- echo " 安装路径: ${NODE_BASE}"
- echo " 数据路径: ${OC_DATA}"
- echo " 离线包: ${offline_dir}"
- echo ""
-
- # [1] 安装 Node.js
- local node_tarball="$offline_dir/node.tar.xz"
- if [ -f "$node_tarball" ]; then
- echo "=== 安装 Node.js (离线) ==="
- rm -rf "$NODE_BASE" 2>/dev/null
- [ -d /overlay/upper ] && rm -rf "/overlay/upper${NODE_BASE}" 2>/dev/null
- ensure_mkdir "$NODE_BASE"
-
- if tar --strip-components=1 -xf "$node_tarball" -C "$NODE_BASE" 2>/dev/null; then
- : # GNU tar
- else
- local tmp_extract="/tmp/node-extract-$$"
- ensure_mkdir "$tmp_extract"
- tar xf "$node_tarball" -C "$tmp_extract"
- local top_dir=$(ls "$tmp_extract" 2>/dev/null | head -1)
- if [ -n "$top_dir" ] && [ -d "$tmp_extract/$top_dir" ]; then
- cp -a "$tmp_extract/$top_dir/." "$NODE_BASE/"
- fi
- rm -rf "$tmp_extract"
- fi
-
- if [ -x "$NODE_BIN" ]; then
- log_info "Node.js $($NODE_BIN --version 2>/dev/null) 安装成功"
- else
- log_error "Node.js 安装失败"
- exit 1
- fi
- else
- log_error "未找到 Node.js 离线包: $node_tarball"
- exit 1
- fi
-
- # [2] 安装 OpenClaw
- local oc_tarball="$offline_dir/openclaw-deps.tar.gz"
- if [ -f "$oc_tarball" ]; then
- echo ""
- echo "=== 安装 OpenClaw (离线) ==="
- rm -rf "$OC_GLOBAL" 2>/dev/null
- [ -d /overlay/upper ] && rm -rf "/overlay/upper${OC_GLOBAL}" 2>/dev/null
- ensure_mkdir "$OC_GLOBAL"
-
- tar xzf "$oc_tarball" -C "$OC_GLOBAL"
-
- 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:]')
- log_info "OpenClaw v${oc_ver} 安装成功"
- else
- log_error "OpenClaw 安装验证失败"
- exit 1
- fi
- else
- log_error "未找到 OpenClaw 离线包: $oc_tarball"
- exit 1
- fi
-
- # [3] 初始化
- init_openclaw
-
- echo ""
- echo "╔══════════════════════════════════════════════════════════════╗"
- echo "║ ✅ 离线安装完成! ║"
- echo "║ ║"
- echo "║ 下一步: ║"
- echo "║ uci set openclaw.main.enabled=1 ║"
- echo "║ uci commit openclaw ║"
- echo "║ /etc/init.d/openclaw enable ║"
- echo "║ /etc/init.d/openclaw start ║"
- echo "║ ║"
- echo "║ 或在 LuCI → 服务 → OpenClaw 中启用 ║"
- echo "╚══════════════════════════════════════════════════════════════╝"
-}
-
-# ── 主入口 ──
-case "${1:-}" in
- setup)
do_setup
;;
- setup-offline)
- do_setup_offline "$@"
;;
check)
do_check
@@ -744,10 +640,8 @@ case "${1:-}" in
do_factory_reset
;;
*)
- echo "用法: openclaw-env {setup|setup-offline|check|upgrade|node|factory-reset}"
echo ""
echo " setup — 完整安装 (下载 Node.js + pnpm + OpenClaw)"
- echo " setup-offline — 离线安装 (从本地文件安装,由 .run 安装器调用)"
echo " check — 检查环境状态"
echo " upgrade — 升级 OpenClaw 到最新版"
echo " node — 仅下载/更新 Node.js"
diff --git a/scripts/build_offline_run.sh b/scripts/build_offline_run.sh
deleted file mode 100755
index 6cc021e..0000000
--- a/scripts/build_offline_run.sh
+++ /dev/null
@@ -1,594 +0,0 @@
-#!/bin/sh
-# ============================================================================
-# OpenClaw 离线 .run 自解压包构建脚本
-# 构建包含所有离线依赖的全架构 .run 安装包
-#
-# 用法:
-# sh scripts/build_offline_run.sh [output_dir]
-#
-# 前置条件:
-# 先运行 sh scripts/download_deps.sh 下载离线依赖到 .offline-cache/
-#
-# 产出:
-# dist/luci-app-openclaw__x86_64-musl_offline.run
-# dist/luci-app-openclaw__aarch64-musl_offline.run
-# ============================================================================
-set -e
-
-SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
-PKG_DIR=$(cd "$SCRIPT_DIR/.." && pwd)
-OUT_DIR="${1:-$PKG_DIR/dist}"
-CACHE_DIR="${CACHE_DIR:-$PKG_DIR/.offline-cache}"
-
-case "$OUT_DIR" in
- /*) ;;
- *) OUT_DIR="$PKG_DIR/$OUT_DIR" ;;
-esac
-case "$CACHE_DIR" in
- /*) ;;
- *) CACHE_DIR="$PKG_DIR/$CACHE_DIR" ;;
-esac
-
-PKG_NAME="luci-app-openclaw"
-PKG_VERSION=$(cat "$PKG_DIR/VERSION" 2>/dev/null | tr -d '[:space:]' || echo "1.0.0")
-NODE_VERSION="${NODE_VERSION:-22.15.1}"
-OC_VERSION=$(cat "$CACHE_DIR/openclaw/VERSION" 2>/dev/null | tr -d '[:space:]' || echo "2026.3.8")
-
-echo "╔══════════════════════════════════════════════════════════════╗"
-echo "║ 构建 OpenClaw 离线 .run 安装包 ║"
-echo "╚══════════════════════════════════════════════════════════════╝"
-echo ""
-echo " 插件版本: v${PKG_VERSION}"
-echo " Node.js: v${NODE_VERSION}"
-echo " OpenClaw: v${OC_VERSION}"
-echo " 缓存目录: ${CACHE_DIR}"
-echo " 输出目录: ${OUT_DIR}"
-echo ""
-
-# 检查缓存目录
-if [ ! -d "$CACHE_DIR/node" ] || [ ! -d "$CACHE_DIR/openclaw" ]; then
- echo "错误: 离线缓存不存在,请先运行:"
- echo " sh scripts/download_deps.sh"
- exit 1
-fi
-
-mkdir -p "$OUT_DIR"
-
-# ── 安装 LuCI 插件文件到暂存区 ──
-install_luci_files() {
- local dest="$1"
-
- mkdir -p "$dest/etc/config"
- cp "$PKG_DIR/root/etc/config/openclaw" "$dest/etc/config/openclaw.default"
-
- mkdir -p "$dest/etc/uci-defaults"
- cp "$PKG_DIR/root/etc/uci-defaults/99-openclaw" "$dest/etc/uci-defaults/"
- chmod +x "$dest/etc/uci-defaults/99-openclaw"
-
- mkdir -p "$dest/etc/init.d"
- cp "$PKG_DIR/root/etc/init.d/openclaw" "$dest/etc/init.d/"
- chmod +x "$dest/etc/init.d/openclaw"
-
- mkdir -p "$dest/usr/bin"
- cp "$PKG_DIR/root/usr/bin/openclaw-env" "$dest/usr/bin/"
- chmod +x "$dest/usr/bin/openclaw-env"
-
- mkdir -p "$dest/usr/lib/lua/luci/controller"
- cp "$PKG_DIR/luasrc/controller/openclaw.lua" "$dest/usr/lib/lua/luci/controller/"
-
- mkdir -p "$dest/usr/lib/lua/luci/model/cbi/openclaw"
- cp "$PKG_DIR/luasrc/model/cbi/openclaw/"*.lua "$dest/usr/lib/lua/luci/model/cbi/openclaw/"
-
- mkdir -p "$dest/usr/lib/lua/luci/view/openclaw"
- cp "$PKG_DIR/luasrc/view/openclaw/"*.htm "$dest/usr/lib/lua/luci/view/openclaw/"
-
- mkdir -p "$dest/usr/share/openclaw"
- cp "$PKG_DIR/VERSION" "$dest/usr/share/openclaw/VERSION"
- cp "$PKG_DIR/root/usr/share/openclaw/oc-config.sh" "$dest/usr/share/openclaw/"
- chmod +x "$dest/usr/share/openclaw/oc-config.sh"
- cp "$PKG_DIR/root/usr/share/openclaw/web-pty.js" "$dest/usr/share/openclaw/"
- cp -r "$PKG_DIR/root/usr/share/openclaw/ui" "$dest/usr/share/openclaw/"
-
- # i18n
- mkdir -p "$dest/usr/lib/lua/luci/i18n"
- if command -v po2lmo >/dev/null 2>&1 && [ -f "$PKG_DIR/po/zh-cn/openclaw.po" ]; then
- po2lmo "$PKG_DIR/po/zh-cn/openclaw.po" "$dest/usr/lib/lua/luci/i18n/openclaw.zh-cn.lmo" 2>/dev/null || true
- fi
-}
-
-# ── 创建离线安装器脚本 ──
-create_offline_installer() {
- local target_arch="$1" # 如 x86_64
- local target_libc="$2" # 如 musl
- local staging="$3"
-
- cat > "$staging/install.sh" << 'INSTALLER_HEADER'
-#!/bin/sh
-# ============================================================================
-# luci-app-openclaw 离线安装器
-# 包含所有依赖,无需联网即可完成完整安装
-# ============================================================================
-set -e
-
-echo "╔══════════════════════════════════════════════════════════════╗"
-echo "║ luci-app-openclaw — OpenClaw AI Gateway 离线安装器 ║"
-echo "║ 包含 Node.js + OpenClaw 运行环境,无需联网 ║"
-echo "╚══════════════════════════════════════════════════════════════╝"
-echo ""
-
-# ── 基本检查 ──
-if [ ! -f /etc/openwrt_release ]; then
- echo "错误: 此安装包仅适用于 OpenWrt/iStoreOS 系统"
- exit 1
-fi
-
-ARCH=$(uname -m)
-TARGET_ARCH="__TARGET_ARCH__"
-TARGET_LIBC="__TARGET_LIBC__"
-
-# 架构检查
-case "$ARCH" in
- x86_64|aarch64) ;;
- *) echo "错误: 不支持的架构 $ARCH (仅支持 x86_64/aarch64)"; exit 1 ;;
-esac
-
-if [ "$ARCH" != "$TARGET_ARCH" ]; then
- echo "错误: 架构不匹配!"
- echo " 当前设备: $ARCH"
- echo " 安装包: ${TARGET_ARCH}-${TARGET_LIBC}"
- echo ""
- echo "请下载对应架构的安装包。"
- exit 1
-fi
-
-# libc 检查
-detect_libc() {
- if ldd --version 2>&1 | grep -qi musl; then
- echo "musl"
- elif [ -f /lib/ld-musl-*.so.1 ] 2>/dev/null; then
- echo "musl"
- elif [ -f /etc/openwrt_release ] || grep -qi "openwrt\|istoreos\|lede" /etc/os-release 2>/dev/null; then
- echo "musl"
- else
- echo "glibc"
- fi
-}
-
-SYS_LIBC=$(detect_libc)
-if [ "$SYS_LIBC" != "$TARGET_LIBC" ]; then
- echo "警告: C 库类型不匹配 (系统: $SYS_LIBC, 安装包: $TARGET_LIBC)"
- echo " 如果安装后 Node.js 无法运行,请下载对应 libc 类型的安装包。"
- echo ""
- printf "是否继续?[y/N] "
- read -r answer
- case "$answer" in
- y|Y|yes|YES) ;;
- *) echo "已取消"; exit 0 ;;
- esac
-fi
-
-# ── 磁盘空间预检查 ──
-echo "检查磁盘空间..."
-# 预估解压后大小: Node.js ~100-200MB + OpenClaw ~200-400MB + 插件 ~1MB
-NEED_MB=500
-# 检查 /opt 所在分区 (OverlayFS 下可能是 /overlay)
-AVAIL_KB=0
-for mount_point in /opt /overlay /; do
- if df "$mount_point" >/dev/null 2>&1; then
- AVAIL_KB=$(df "$mount_point" 2>/dev/null | tail -1 | awk '{print $4}')
- break
- fi
-done
-AVAIL_MB=$((AVAIL_KB / 1024))
-if [ "$AVAIL_MB" -lt "$NEED_MB" ] 2>/dev/null; then
- echo "警告: 可用空间不足!"
- echo " 需要: 至少 ${NEED_MB}MB"
- echo " 当前: ${AVAIL_MB}MB 可用"
- echo ""
- printf "是否继续?[y/N] "
- read -r answer
- case "$answer" in
- y|Y|yes|YES) ;;
- *) echo "已取消"; exit 0 ;;
- esac
-fi
-
-# ── OverlayFS 修复 ──
-_oc_fix_opt() {
- mkdir -p /opt/openclaw/.probe 2>/dev/null && { rmdir /opt/openclaw/.probe 2>/dev/null; return 0; }
- if [ -d /overlay/upper/opt ]; then
- mkdir -p /overlay/upper/opt/openclaw 2>/dev/null
- mount --bind /overlay/upper/opt /opt 2>/dev/null && return 0
- fi
- return 1
-}
-_oc_fix_opt || true
-
-NODE_BASE="/opt/openclaw/node"
-OC_GLOBAL="/opt/openclaw/global"
-OC_DATA="/opt/openclaw/data"
-
-ensure_mkdir() {
- local target="$1"
- [ -d "$target" ] && return 0
- if ! mkdir -p "$target" 2>/dev/null; then
- echo " [✗] 无法创建目录: $target"
- return 1
- fi
-}
-
-# ── 解压安装 ──
-
-# 先停止已有服务 (避免文件被占用导致覆盖安装失败)
-if [ -x /etc/init.d/openclaw ]; then
- echo "停止已有服务..."
- /etc/init.d/openclaw stop 2>/dev/null || true
- # 等待进程退出和端口释放
- sleep 2
- # 确保 gateway 子进程也已退出
- for pid in $(pgrep -f "openclaw-gateway" 2>/dev/null); do
- kill "$pid" 2>/dev/null
- done
- sleep 1
-fi
-
-echo ""
-echo "正在提取安装文件..."
-
-# 解压 payload (从 MARKER 行之后)
-ARCHIVE=$(awk '/^__ARCHIVE_BELOW__/ {print NR + 1; exit 0; }' "$0")
-EXTRACT_DIR=$(mktemp -d)
-trap "rm -rf '$EXTRACT_DIR'" EXIT
-
-tail -n +$ARCHIVE "$0" | tar xzf - -C "$EXTRACT_DIR" 2>/dev/null
-
-# ── [Step 1/5] 安装 LuCI 插件文件 ──
-echo ""
-echo "[1/5] 安装 LuCI 插件..."
-
-# 复制插件文件到系统 (从 luci-files/ 子目录)
-if [ -d "$EXTRACT_DIR/luci-files" ]; then
- cp -a "$EXTRACT_DIR/luci-files/." / 2>/dev/null
-fi
-
-# UCI 配置文件保护
-if [ -f /etc/config/openclaw ] && [ -f /etc/config/openclaw.default ]; then
- rm -f /etc/config/openclaw.default
-elif [ -f /etc/config/openclaw.default ]; then
- mv /etc/config/openclaw.default /etc/config/openclaw
-fi
-
-echo " [✓] LuCI 插件已安装"
-
-# ── [Step 2/5] 安装 Node.js ──
-echo ""
-echo "[2/5] 安装 Node.js..."
-
-NODE_TARBALL="$EXTRACT_DIR/node.tar.xz"
-if [ -f "$NODE_TARBALL" ]; then
- # 清理旧安装
- rm -rf "$NODE_BASE" 2>/dev/null
- [ -d /overlay/upper ] && rm -rf "/overlay/upper${NODE_BASE}" 2>/dev/null
- ensure_mkdir "$NODE_BASE"
-
- # 解压 Node.js (兼容 BusyBox tar)
- if tar --strip-components=1 -xf "$NODE_TARBALL" -C "$NODE_BASE" 2>/dev/null; then
- : # GNU tar
- else
- # BusyBox tar 回退
- local tmp_node="/tmp/node-extract-$$"
- ensure_mkdir "$tmp_node"
- tar xf "$NODE_TARBALL" -C "$tmp_node"
- local top_dir=$(ls "$tmp_node" 2>/dev/null | head -1)
- if [ -n "$top_dir" ] && [ -d "$tmp_node/$top_dir" ]; then
- cp -a "$tmp_node/$top_dir/." "$NODE_BASE/"
- fi
- rm -rf "$tmp_node"
- fi
-
- if [ -x "$NODE_BASE/bin/node" ]; then
- echo " [✓] Node.js $($NODE_BASE/bin/node --version 2>/dev/null) 已安装"
- else
- echo " [✗] Node.js 安装失败"
- exit 1
- fi
-else
- echo " [✗] 安装包中未找到 Node.js"
- exit 1
-fi
-
-# ── [Step 3/5] 安装 OpenClaw ──
-echo ""
-echo "[3/5] 安装 OpenClaw..."
-
-OC_DEPS_TARBALL="$EXTRACT_DIR/openclaw-deps.tar.gz"
-if [ -f "$OC_DEPS_TARBALL" ]; then
- rm -rf "$OC_GLOBAL" 2>/dev/null
- [ -d /overlay/upper ] && rm -rf "/overlay/upper${OC_GLOBAL}" 2>/dev/null
- ensure_mkdir "$OC_GLOBAL"
-
- tar xzf "$OC_DEPS_TARBALL" -C "$OC_GLOBAL"
-
- # 验证 openclaw 入口
- OC_ENTRY=""
- for d in "$OC_GLOBAL/lib/node_modules/openclaw" "$OC_GLOBAL/node_modules/openclaw"; do
- if [ -f "${d}/openclaw.mjs" ]; then
- OC_ENTRY="${d}/openclaw.mjs"
- break
- elif [ -f "${d}/dist/cli.js" ]; then
- OC_ENTRY="${d}/dist/cli.js"
- break
- fi
- done
-
- if [ -n "$OC_ENTRY" ]; then
- OC_VER=$("$NODE_BASE/bin/node" "$OC_ENTRY" --version 2>/dev/null | tr -d '[:space:]' || echo "unknown")
- echo " [✓] OpenClaw v${OC_VER} 已安装"
- else
- echo " [✗] OpenClaw 安装验证失败"
- exit 1
- fi
-else
- echo " [✗] 安装包中未找到 OpenClaw 依赖"
- exit 1
-fi
-
-# ── [Step 4/5] 初始化 OpenClaw ──
-echo ""
-echo "[4/5] 初始化 OpenClaw..."
-
-ensure_mkdir "$OC_DATA/.openclaw"
-
-# 创建 openclaw 系统用户 (如果不存在)
-if ! id openclaw >/dev/null 2>&1; then
- # OpenWrt 使用 BusyBox adduser
- if command -v adduser >/dev/null 2>&1; then
- adduser -D -H -s /bin/false -h "$OC_DATA" openclaw 2>/dev/null || true
- fi
-fi
-
-# 运行 onboard
-if [ -n "$OC_ENTRY" ] && [ -x "$NODE_BASE/bin/node" ]; then
- HOME="$OC_DATA" \
- OPENCLAW_HOME="$OC_DATA" \
- OPENCLAW_STATE_DIR="${OC_DATA}/.openclaw" \
- OPENCLAW_CONFIG_PATH="${OC_DATA}/.openclaw/openclaw.json" \
- "$NODE_BASE/bin/node" "$OC_ENTRY" onboard --non-interactive --accept-risk --tools-profile coding 2>/dev/null || true
-fi
-
-# 设置权限
-chown -R openclaw:openclaw "$OC_DATA" 2>/dev/null || true
-chown -R openclaw:openclaw "$OC_GLOBAL" 2>/dev/null || true
-chown -R openclaw:openclaw "$NODE_BASE" 2>/dev/null || true
-
-echo " [✓] 初始化完成"
-
-# ── [Step 5/5] 注册 opkg + 启动服务 ──
-echo ""
-echo "[5/5] 注册到系统..."
-
-# 注册到 opkg
-PKG="luci-app-openclaw"
-PKG_VER="__PKG_VERSION__"
-INFO_DIR="/usr/lib/opkg/info"
-STATUS_FILE="/usr/lib/opkg/status"
-INSTALL_TIME=$(date +%s)
-
-mkdir -p "$INFO_DIR"
-
-cat > "$INFO_DIR/$PKG.control" << CTLEOF
-Package: $PKG
-Version: $PKG_VER
-Depends: luci-compat, luci-base
-Section: luci
-Architecture: all
-Installed-Size: 0
-Description: OpenClaw AI Gateway — LuCI 界面 (离线安装)
-CTLEOF
-
-cat > "$INFO_DIR/$PKG.list" << LISTEOF
-__FILE_LIST__
-LISTEOF
-
-cat > "$INFO_DIR/$PKG.prerm" << 'RMEOF'
-#!/bin/sh
-/etc/init.d/openclaw stop 2>/dev/null
-/etc/init.d/openclaw disable 2>/dev/null
-exit 0
-RMEOF
-chmod +x "$INFO_DIR/$PKG.prerm"
-
-# 更新 opkg status
-if [ -f "$STATUS_FILE" ]; then
- awk -v pkg="$PKG" '
- BEGIN { skip=0 }
- /^Package:/ { skip=($2==pkg) }
- /^$/ { if(skip){skip=0; next} }
- !skip { print }
- ' "$STATUS_FILE" > "${STATUS_FILE}.tmp"
- mv "${STATUS_FILE}.tmp" "$STATUS_FILE"
-fi
-
-cat >> "$STATUS_FILE" << STEOF
-
-Package: $PKG
-Version: $PKG_VER
-Depends: luci-compat, luci-base
-Status: install user installed
-Architecture: all
-Conffiles:
- /etc/config/openclaw 0
-Installed-Time: $INSTALL_TIME
-STEOF
-
-echo " [✓] 已注册到 opkg"
-
-# 写入离线安装标记 (供 LuCI 界面识别安装方式)
-cat > /usr/share/openclaw/.offline-install << OFFEOF
-type=offline
-date=$(date '+%Y-%m-%d %H:%M:%S' 2>/dev/null || date)
-arch=${TARGET_ARCH}-${TARGET_LIBC}
-node=$($NODE_BASE/bin/node --version 2>/dev/null || echo unknown)
-openclaw=${OC_VER:-unknown}
-plugin=__PKG_VERSION__
-OFFEOF
-echo " [✓] 离线安装标记已写入"
-
-# 执行 uci-defaults
-if [ -f /etc/uci-defaults/99-openclaw ]; then
- ( . /etc/uci-defaults/99-openclaw ) && rm -f /etc/uci-defaults/99-openclaw
-fi
-
-# 清除 LuCI 缓存
-rm -f /tmp/luci-indexcache /tmp/luci-modulecache/* 2>/dev/null
-rm -f /tmp/luci-indexcache.*.json 2>/dev/null
-
-# 启用并启动服务
-/etc/init.d/openclaw enable 2>/dev/null || true
-uci set openclaw.main.enabled=1 2>/dev/null || true
-uci commit openclaw 2>/dev/null || true
-
-# 清理
-rm -rf "$EXTRACT_DIR"
-trap - EXIT
-
-echo ""
-echo "╔══════════════════════════════════════════════════════════════╗"
-echo "║ ✅ 离线安装完成! ║"
-echo "║ ║"
-echo "║ Node.js + OpenClaw + LuCI 插件已全部安装 ║"
-echo "║ 无需再运行 openclaw-env setup ║"
-echo "║ ║"
-echo "║ 下一步: ║"
-echo "║ 访问 LuCI → 服务 → OpenClaw 进行配置 ║"
-echo "║ 或执行: /etc/init.d/openclaw start ║"
-echo "║ ║"
-echo "║ 配置模型 API 密钥后即可使用,全程无需联网! ║"
-echo "╚══════════════════════════════════════════════════════════════╝"
-echo ""
-
-exit 0
-__ARCHIVE_BELOW__
-INSTALLER_HEADER
-}
-
-# ============================================================================
-# 为每种架构构建 .run
-# ============================================================================
-build_one_variant() {
- local label="$1" # 如 x86_64-musl
- local uname_arch="$2" # 如 x86_64
- local node_suffix="$3" # 如 linux-x64-musl
- local libc="$4" # 如 musl
-
- echo ""
- echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
- echo " 构建: ${label}"
- echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
-
- local node_tarball="$CACHE_DIR/node/node-v${NODE_VERSION}-${node_suffix}.tar.xz"
- if [ ! -f "$node_tarball" ]; then
- echo " [!] 跳过: 未找到 Node.js 包 $(basename "$node_tarball")"
- return 1
- fi
-
- # 查找 OpenClaw 依赖包
- local oc_deps=""
- for f in "$CACHE_DIR/openclaw/openclaw-deps-"*.tar.gz; do
- [ -f "$f" ] && oc_deps="$f" && break
- done
- if [ -z "$oc_deps" ]; then
- echo " [!] 跳过: 未找到 OpenClaw 依赖包"
- return 1
- fi
-
- # 创建临时暂存区
- local staging=$(mktemp -d)
-
- # [1] 准备 payload 结构
- local payload="$staging/payload"
- mkdir -p "$payload/luci-files"
-
- echo " [1/5] 安装 LuCI 插件文件..."
- install_luci_files "$payload/luci-files"
-
- echo " [2/5] 复制 Node.js 包..."
- cp "$node_tarball" "$payload/node.tar.xz"
-
- echo " [3/5] 复制 OpenClaw 依赖包..."
- cp "$oc_deps" "$payload/openclaw-deps.tar.gz"
-
- # [2] 生成文件列表
- echo " [4/5] 生成安装器..."
- local file_list=$(cd "$payload/luci-files" && find . -type f | sed 's|^\./|/|' | sed 's|/etc/config/openclaw.default|/etc/config/openclaw|' | sort)
-
- # 创建安装器
- create_offline_installer "$uname_arch" "$libc" "$staging"
-
- # 替换占位符
- sed -i "s|__TARGET_ARCH__|${uname_arch}|g" "$staging/install.sh"
- sed -i "s|__TARGET_LIBC__|${libc}|g" "$staging/install.sh"
- sed -i "s|__PKG_VERSION__|${PKG_VERSION}|g" "$staging/install.sh"
-
- # 替换文件列表
- {
- sed '/__FILE_LIST__/,$d' "$staging/install.sh"
- echo "$file_list"
- sed '1,/__FILE_LIST__/d' "$staging/install.sh"
- } > "$staging/install_final.sh"
- mv "$staging/install_final.sh" "$staging/install.sh"
-
- # [3] 打包 payload (gzip 压缩, 减小 .run 文件体积)
- echo " [5/5] 打包..."
- (cd "$payload" && tar czf "$staging/payload.tar.gz" .)
-
- # [4] 组合: installer + payload
- local run_file="$OUT_DIR/${PKG_NAME}_${PKG_VERSION}_${label}_offline.run"
- cat "$staging/install.sh" "$staging/payload.tar.gz" > "$run_file"
- chmod +x "$run_file"
-
- local file_size=$(wc -c < "$run_file" | tr -d ' ')
- local file_size_mb=$((file_size / 1024 / 1024))
-
- echo " [✓] ${run_file}"
- echo " 大小: ${file_size_mb}MB (${file_size} bytes)"
-
- # 生成 SHA256
- sha256sum "$run_file" > "${run_file}.sha256" 2>/dev/null || true
-
- rm -rf "$staging"
- return 0
-}
-
-# ── 主构建流程 ──
-SUCCESS=0
-FAILED=0
-
-# 使用 for 循环避免 BusyBox ash 的 IFS/read 管道问题
-for variant in \
- "x86_64-musl:x86_64:linux-x64-musl:musl" \
- "aarch64-musl:aarch64:linux-arm64-musl:musl" \
-; do
- label=$(echo "$variant" | cut -d: -f1)
- uname_arch=$(echo "$variant" | cut -d: -f2)
- node_suffix=$(echo "$variant" | cut -d: -f3)
- libc=$(echo "$variant" | cut -d: -f4)
-
- if build_one_variant "$label" "$uname_arch" "$node_suffix" "$libc"; then
- SUCCESS=$((SUCCESS + 1))
- else
- FAILED=$((FAILED + 1))
- fi
-done
-
-echo ""
-echo "╔══════════════════════════════════════════════════════════════╗"
-echo "║ 构建完成 ║"
-echo "╚══════════════════════════════════════════════════════════════╝"
-echo ""
-echo "输出目录: $OUT_DIR"
-echo ""
-ls -lh "$OUT_DIR/"*_offline.run 2>/dev/null || echo "(无输出文件)"
-echo ""
-echo "安装方法: 将 .run 文件传输到路由器后执行:"
-echo " sh luci-app-openclaw_*_offline.run"
diff --git a/scripts/download_deps.sh b/scripts/download_deps.sh
deleted file mode 100755
index 8b0b766..0000000
--- a/scripts/download_deps.sh
+++ /dev/null
@@ -1,304 +0,0 @@
-#!/bin/sh
-# ============================================================================
-# 离线依赖预下载脚本 (在有网络的构建机上运行)
-# 为所有支持的架构下载 Node.js + OpenClaw + pnpm
-#
-# 用法:
-# sh scripts/download_deps.sh [cache_dir]
-#
-# 产出目录结构:
-# cache_dir/
-# node/
-# node-v22.15.1-linux-x64-musl.tar.xz
-# node-v22.15.1-linux-arm64-musl.tar.xz
-# openclaw/
-# openclaw-deps-v.tar.gz (完整 node_modules, 跨架构通用, ~150MB)
-# ============================================================================
-set -e
-
-SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
-PKG_DIR=$(cd "$SCRIPT_DIR/.." && pwd)
-CACHE_DIR="${1:-$PKG_DIR/.offline-cache}"
-
-# 确保 CACHE_DIR 是绝对路径
-case "$CACHE_DIR" in
- /*) ;;
- *) CACHE_DIR="$PKG_DIR/$CACHE_DIR" ;;
-esac
-
-# ── 版本配置 (与 openclaw-env 保持一致) ──
-NODE_VERSION="${NODE_VERSION:-22.15.1}"
-OC_VERSION="${OC_VERSION:-2026.3.8}"
-
-# ── 下载镜像 ──
-NODE_MIRROR="${NODE_MIRROR:-https://nodejs.org/dist}"
-NODE_MIRROR_CN="https://npmmirror.com/mirrors/node"
-NODE_MUSL_MIRROR="https://unofficial-builds.nodejs.org/download/release"
-NODE_SELF_HOST="https://github.com/10000ge10000/luci-app-openclaw/releases/download/node-bins"
-NPM_REGISTRY="${NPM_REGISTRY:-https://registry.npmjs.org}"
-
-log_info() { echo " [✓] $1"; }
-log_warn() { echo " [!] $1"; }
-log_error() { echo " [✗] $1"; }
-
-# 自动检测 Node.js / npm (兼容 OpenWrt 上已安装的 openclaw 环境)
-if ! command -v node >/dev/null 2>&1; then
- for try_path in /opt/openclaw/node/bin /usr/local/bin; do
- if [ -x "$try_path/node" ]; then
- export PATH="$try_path:$PATH"
- log_info "检测到 Node.js: $try_path/node"
- break
- fi
- done
-fi
-
-# 下载文件 (支持 curl 和 wget)
-download_file() {
- local url="$1" dest="$2"
- if [ -f "$dest" ]; then
- local fsize=$(wc -c < "$dest" 2>/dev/null || echo 0)
- if [ "$fsize" -gt 1000000 ] 2>/dev/null; then
- log_info "已缓存: $(basename "$dest") ($(du -h "$dest" | cut -f1))"
- return 0
- fi
- fi
- echo " 下载: $url"
- if curl -fSL --connect-timeout 30 --max-time 600 -o "$dest" "$url" 2>/dev/null; then
- return 0
- elif wget -q --timeout=30 -O "$dest" "$url" 2>/dev/null; then
- return 0
- fi
- rm -f "$dest"
- return 1
-}
-
-# ============================================================================
-# Phase 1: 下载 Node.js (全架构)
-# ============================================================================
-download_all_node() {
- echo ""
- echo "╔══════════════════════════════════════════════════════════════╗"
- printf "║ [1/3] 下载 Node.js v%-8s (musl 架构) ║\n" "$NODE_VERSION"
- echo "╚══════════════════════════════════════════════════════════════╝"
- echo ""
-
- local node_dir="$CACHE_DIR/node"
- mkdir -p "$node_dir"
-
- # x86_64 musl
- echo "=== x86_64 musl ==="
- local x64_musl="node-v${NODE_VERSION}-linux-x64-musl.tar.xz"
- download_file "${NODE_MUSL_MIRROR}/v${NODE_VERSION}/${x64_musl}" "$node_dir/$x64_musl" || \
- download_file "${NODE_MIRROR_CN}/v${NODE_VERSION}/${x64_musl}" "$node_dir/$x64_musl" || \
- log_error "x86_64 musl 下载失败"
-
- # aarch64 musl (项目自托管)
- echo "=== aarch64 musl ==="
- local arm64_musl="node-v${NODE_VERSION}-linux-arm64-musl.tar.xz"
- download_file "${NODE_SELF_HOST}/${arm64_musl}" "$node_dir/$arm64_musl" || \
- download_file "${NODE_MUSL_MIRROR}/v${NODE_VERSION}/${arm64_musl}" "$node_dir/$arm64_musl" || \
- log_error "aarch64 musl 下载失败"
-
- echo ""
- echo "Node.js 下载完成:"
- ls -lh "$node_dir/"*.tar.xz 2>/dev/null || echo " (无文件)"
-}
-
-# ============================================================================
-# Phase 2: 下载并预装 OpenClaw + 依赖
-# ============================================================================
-download_openclaw_deps() {
- echo ""
- echo "╔══════════════════════════════════════════════════════════════╗"
- printf "║ [2/3] 下载 OpenClaw v%-8s + 全部依赖 ║\n" "$OC_VERSION"
- echo "╚══════════════════════════════════════════════════════════════╝"
- echo ""
-
- local oc_dir="$CACHE_DIR/openclaw"
- mkdir -p "$oc_dir"
-
- # 检查 npm 是否可用 (构建机上需要 node + npm)
- local NPM_CMD=""
- if command -v npm >/dev/null 2>&1; then
- NPM_CMD="npm"
- elif [ -x /opt/openclaw/node/bin/npm ]; then
- # OpenWrt 上 npm wrapper 可能需要显式 node 调用
- NPM_CMD="/opt/openclaw/node/bin/node /opt/openclaw/node/bin/npm"
- else
- log_error "构建机上需要 npm"
- log_error "请执行: apt install -y nodejs npm 或 apk add nodejs npm"
- log_error "或者确保 /opt/openclaw/node 中有 Node.js"
- exit 1
- fi
- log_info "使用 npm: $NPM_CMD"
-
- # 方案: 使用 npm install 到临时目录,然后打包整个 node_modules
- # 这是最可靠的方式,确保所有依赖树完整
-
- local tmp_install="/tmp/openclaw-offline-$$"
- trap "rm -rf '$tmp_install'" EXIT
-
- # ── 为每种架构生成预安装包 ──
- # 注意: openclaw 的依赖树中可能包含平台特定的 optional dependencies
- # musl 环境下用 --ignore-scripts 跳过原生编译
- # 对于离线包,我们在构建机上安装后直接打包 node_modules
-
- # 通用安装 (忽略平台特定编译脚本)
- echo "=== 安装 OpenClaw 依赖 (通用包) ==="
- mkdir -p "$tmp_install/global"
-
- # ── 强制 npm 使用 musl 平台检测 ──
- # 目标系统是 OpenWrt/iStoreOS (musl libc),无论构建机是什么系统
- # 在 glibc 系统 (如 GitHub Actions ubuntu-latest) 上,npm 默认安装 *-gnu 变体
- # 的原生可选依赖 (如 @napi-rs/canvas-linux-x64-gnu),比 *-musl 变体大得多
- # 通过设置 npm_config_libc=musl 强制安装 musl 变体,确保跨平台一致的产物大小
- export npm_config_os=linux
- export npm_config_libc=musl
-
- echo " 正在用 npm 安装 openclaw@${OC_VERSION}..."
- echo " npm 平台覆盖: os=${npm_config_os}, libc=${npm_config_libc}"
- $NPM_CMD install -g "openclaw@${OC_VERSION}" \
- --prefix="$tmp_install/global" \
- --ignore-scripts \
- --omit=dev \
- --omit=optional \
- --no-optional \
- --registry="$NPM_REGISTRY" 2>&1 | tail -20
-
- # 验证安装
- local oc_entry=""
- for d in "$tmp_install/global/lib/node_modules/openclaw" "$tmp_install/global/node_modules/openclaw"; do
- if [ -f "${d}/openclaw.mjs" ]; then
- oc_entry="${d}/openclaw.mjs"
- break
- elif [ -f "${d}/dist/cli.js" ]; then
- oc_entry="${d}/dist/cli.js"
- break
- fi
- done
-
- if [ -z "$oc_entry" ]; then
- log_error "OpenClaw 安装验证失败"
- echo "目录内容:"
- find "$tmp_install/global" -maxdepth 4 -type d 2>/dev/null | head -30
- exit 1
- fi
-
- log_info "OpenClaw 安装验证通过: $oc_entry"
-
- # 获取实际安装的版本号
- local actual_ver=""
- local oc_pkg_dir="$(dirname "$oc_entry")"
- if [ -f "$oc_pkg_dir/package.json" ]; then
- actual_ver=$(node -e "console.log(require('$oc_pkg_dir/package.json').version)" 2>/dev/null || echo "$OC_VERSION")
- fi
- echo " 实际版本: v${actual_ver:-$OC_VERSION}"
-
- # ── 打包为通用 tarball ──
- # 不精简 node_modules,保留全部功能(飞书/Slack/Discord 等平台 SDK)
- # 依靠 gzip 压缩控制包大小: ~670MB → ~150MB
- # 因为 openclaw 是纯 JS 包 (使用 --ignore-scripts),node_modules 跨架构通用
- echo ""
- echo "=== 打包 OpenClaw 依赖 ==="
- local install_size=$(du -sm "$tmp_install/global" 2>/dev/null | awk '{print $1}')
- local tarball="$oc_dir/openclaw-deps-v${actual_ver:-$OC_VERSION}.tar.gz"
- echo " 正在压缩 ${install_size}MB 数据到 tar.gz (可能需要数分钟)..."
- # 注意: 不在子 shell 中用 set -e, 避免 tar 在处理损坏的符号链接时意外退出
- # --warning=no-file-changed: 忽略打包过程中文件被修改的警告
- if ! tar czf "$tarball" -C "$tmp_install/global" . 2>&1; then
- log_error "tar 打包失败"
- rm -f "$tarball"
- exit 1
- fi
- # 验证 gzip 完整性
- if ! gzip -t "$tarball" 2>/dev/null; then
- log_error "tar.gz 文件完整性检查失败!"
- rm -f "$tarball"
- exit 1
- fi
- local tgz_size=$(du -h "$tarball" | cut -f1)
- log_info "依赖包: $tarball ($tgz_size) [完整性已验证]"
-
- # 保存版本号
- echo "${actual_ver:-$OC_VERSION}" > "$oc_dir/VERSION"
-
- rm -rf "$tmp_install"
- # 清除之前的 trap
- trap - EXIT
-}
-
-# ============================================================================
-# Phase 3: 生成清单
-# ============================================================================
-generate_manifest() {
- echo ""
- echo "╔══════════════════════════════════════════════════════════════╗"
- echo "║ [3/3] 生成构建清单 ║"
- echo "╚══════════════════════════════════════════════════════════════╝"
- echo ""
-
- local manifest="$CACHE_DIR/manifest.txt"
- local oc_ver=$(cat "$CACHE_DIR/openclaw/VERSION" 2>/dev/null || echo "$OC_VERSION")
-
- cat > "$manifest" << EOF
-# OpenClaw Offline Bundle - 依赖清单
-# 生成时间: $(date -Iseconds 2>/dev/null || date)
-# Node.js: v${NODE_VERSION}
-# OpenClaw: v${oc_ver}
-
-[node]
-EOF
-
- # 列出 Node.js 包
- for f in "$CACHE_DIR/node/"*.tar.xz; do
- [ -f "$f" ] || continue
- local fname=$(basename "$f")
- local fsize=$(du -h "$f" | cut -f1)
- local sha256=$(sha256sum "$f" 2>/dev/null | awk '{print $1}' || echo "N/A")
- echo "${fname} size=${fsize} sha256=${sha256}" >> "$manifest"
- done
-
- echo "" >> "$manifest"
- echo "[openclaw]" >> "$manifest"
-
- # 列出 OpenClaw 包
- for f in "$CACHE_DIR/openclaw/"*.tar.gz; do
- [ -f "$f" ] || continue
- local fname=$(basename "$f")
- local fsize=$(du -h "$f" | cut -f1)
- local sha256=$(sha256sum "$f" 2>/dev/null | awk '{print $1}' || echo "N/A")
- echo "${fname} size=${fsize} sha256=${sha256}" >> "$manifest"
- done
-
- echo ""
- echo "=== 依赖清单 ==="
- cat "$manifest"
- echo ""
-
- # 统计总大小
- local total_size=$(du -sh "$CACHE_DIR" 2>/dev/null | awk '{print $1}')
- echo "缓存目录: $CACHE_DIR"
- echo "总大小: $total_size"
-}
-
-# ── 主入口 ──
-echo "╔══════════════════════════════════════════════════════════════╗"
-echo "║ OpenClaw 离线依赖下载器 ║"
-echo "╚══════════════════════════════════════════════════════════════╝"
-echo ""
-echo " Node.js: v${NODE_VERSION}"
-echo " OpenClaw: v${OC_VERSION}"
-echo " 缓存目录: ${CACHE_DIR}"
-echo " npm 源: ${NPM_REGISTRY}"
-echo ""
-
-mkdir -p "$CACHE_DIR"
-
-download_all_node
-download_openclaw_deps
-generate_manifest
-
-echo ""
-echo "╔══════════════════════════════════════════════════════════════╗"
-echo "║ ✅ 依赖下载完成!现在可以运行 build_offline_run.sh ║"
-echo "╚══════════════════════════════════════════════════════════════╝"
diff --git a/scripts/gen-release-body.sh b/scripts/gen-release-body.sh
index 7bf0e28..5f314db 100644
--- a/scripts/gen-release-body.sh
+++ b/scripts/gen-release-body.sh
@@ -35,13 +35,6 @@ fi
echo "opkg install luci-app-openclaw_${VER}-1_all.ipk"
echo '```'
echo ''
- echo '**离线安装** (无需联网,包含全部依赖)'
- echo '```bash'
- echo '# 将对应架构的 *_offline.run 传到路由器'
- echo 'scp luci-app-openclaw_*_offline.run root@路由器IP:/tmp/'
- echo 'ssh root@路由器IP "sh /tmp/luci-app-openclaw_*_offline.run"'
- echo '```'
- echo ''
echo '[使用文档](https://github.com/10000ge10000/luci-app-openclaw#readme) · [问题反馈](https://github.com/10000ge10000/luci-app-openclaw/issues) · [B站](https://space.bilibili.com/59438380) · [博客](https://blog.910501.xyz/)'
} > "${OUT_DIR}/${VER}.md"
diff --git a/scripts/upload_openlist.sh b/scripts/upload_openlist.sh
index b410374..df1c6ac 100755
--- a/scripts/upload_openlist.sh
+++ b/scripts/upload_openlist.sh
@@ -1,221 +1,49 @@
#!/bin/sh
# ============================================================================
-# OpenList 网盘上传脚本
-# 将构建产物上传到 OpenList (AList) 网盘,便于国内用户下载
-#
-# 用法:
-# sh scripts/upload_openlist.sh [dist_dir]
-#
-# 环境变量 (必须):
-# OPENLIST_URL — OpenList 服务地址 (如 https://pan.example.com)
-# OPENLIST_USER — 登录用户名
-# OPENLIST_PASS — 登录密码
-#
-# 环境变量 (可选):
-# OPENLIST_PATH — 上传根路径 (默认: /Quark)
-# OPENLIST_TOKEN — 直接提供 token, 跳过登录
-# UPLOAD_MODE — 上传模式: offline / online / auto (默认 auto)
-# auto 模式自动检测: 有 *_offline.run 则离线, 有 .run/.ipk 则在线
+# 上传构建产物到 OpenList 软件源
# ============================================================================
set -e
-SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
-PKG_DIR=$(cd "$SCRIPT_DIR/.." && pwd)
-DIST_DIR="${1:-$PKG_DIR/dist}"
-
-case "$DIST_DIR" in
- /*) ;;
- *) DIST_DIR="$PKG_DIR/$DIST_DIR" ;;
-esac
-
-# 检查必要的环境变量
-if [ -z "$OPENLIST_URL" ]; then
- echo "错误: 请设置 OPENLIST_URL 环境变量"
- echo " 例: export OPENLIST_URL=https://pan.example.com"
- exit 1
+if [ -z "$OPENLIST_TOKEN" ]; then
+ echo "错误: 未提供 OPENLIST_TOKEN 环境变量"
+ exit 1
fi
-if [ -z "$OPENLIST_TOKEN" ] && { [ -z "$OPENLIST_USER" ] || [ -z "$OPENLIST_PASS" ]; }; then
- echo "错误: 请设置登录凭据"
- echo " 方式一: export OPENLIST_USER=xxx OPENLIST_PASS=xxx"
- echo " 方式二: export OPENLIST_TOKEN=xxx"
- exit 1
+DIST_DIR="${1:-dist}"
+if [ ! -d "$DIST_DIR" ]; then
+ echo "错误: 产物目录不存在: $DIST_DIR"
+ exit 1
fi
-UPLOAD_ROOT="${OPENLIST_PATH:-/Quark}"
-UPLOAD_MODE="${UPLOAD_MODE:-auto}"
-PKG_VERSION=$(cat "$PKG_DIR/VERSION" 2>/dev/null | tr -d '[:space:]' || echo "unknown")
-
-# 去除路径末尾的 /
-UPLOAD_ROOT="${UPLOAD_ROOT%/}"
-
-# 自动检测上传模式
-if [ "$UPLOAD_MODE" = "auto" ]; then
- if ls "$DIST_DIR"/*_offline.run >/dev/null 2>&1; then
- UPLOAD_MODE="offline"
- elif ls "$DIST_DIR"/*.run >/dev/null 2>&1 || ls "$DIST_DIR"/*.ipk >/dev/null 2>&1; then
- UPLOAD_MODE="online"
- else
- echo "错误: 无法自动检测上传模式,dist 目录中无可识别文件"
- exit 1
- fi
-fi
-
-# 根据模式设置子目录和文件匹配规则
-case "$UPLOAD_MODE" in
- offline)
- UPLOAD_SUBDIR="openclaw-离线安装"
- ;;
- online)
- UPLOAD_SUBDIR="openclaw-在线安装"
- ;;
- *)
- echo "错误: 无效的 UPLOAD_MODE: $UPLOAD_MODE (可选: offline / online / auto)"
- exit 1
- ;;
-esac
-
-OPENLIST_URL="${OPENLIST_URL%/}"
-
-log_info() { echo " [✓] $1"; }
-log_warn() { echo " [!] $1"; }
-log_error() { echo " [✗] $1"; }
-
-# ── 获取 Token ──
-get_token() {
- if [ -n "$OPENLIST_TOKEN" ]; then
- echo "$OPENLIST_TOKEN"
- return
- fi
-
- log_info "正在登录 OpenList..." >&2
- local resp
- resp=$(curl -s -X POST "${OPENLIST_URL}/api/auth/login" \
- -H "Content-Type: application/json" \
- -d "{\"username\":\"${OPENLIST_USER}\",\"password\":\"${OPENLIST_PASS}\"}" 2>/dev/null)
-
- local token
- # 尝试解析 JSON 响应 (兼容多种 alist 版本)
- # alist v3 响应格式: {"code":200,"data":{"token":"xxx"},"message":"success"}
- if command -v jq >/dev/null 2>&1; then
- token=$(echo "$resp" | jq -r '.data.token // empty' 2>/dev/null)
- else
- # 无 jq 时用 grep/sed 提取
- token=$(echo "$resp" | grep -o '"token":"[^"]*"' | sed 's/"token":"//;s/"//')
- fi
-
- if [ -z "$token" ]; then
- log_error "登录失败" >&2
- echo " 响应: $resp" >&2
- exit 1
- fi
-
- log_info "登录成功" >&2
- echo "$token"
-}
-
-# ── 创建远程目录 ──
-create_remote_dir() {
- local token="$1"
- local remote_path="$2"
-
- curl -s -X POST "${OPENLIST_URL}/api/fs/mkdir" \
- -H "Authorization: ${token}" \
- -H "Content-Type: application/json" \
- -d "{\"path\":\"${remote_path}\"}" >/dev/null 2>&1 || true
-}
-
-# ── 上传单个文件 ──
-upload_file() {
- local token="$1"
- local local_file="$2"
- local remote_path="$3"
- local filename=$(basename "$local_file")
- local fsize=$(du -h "$local_file" | cut -f1)
-
- echo " 上传: ${filename} (${fsize})..."
-
- local resp
- resp=$(curl -s -X PUT "${OPENLIST_URL}/api/fs/put" \
- -H "Authorization: ${token}" \
- -H "File-Path: ${remote_path}/${filename}" \
- -H "Content-Type: application/octet-stream" \
- --data-binary "@${local_file}" \
- --max-time 3600 2>/dev/null)
-
- # 检查响应
- local code=""
- if command -v jq >/dev/null 2>&1; then
- code=$(echo "$resp" | jq -r '.code // empty' 2>/dev/null)
- else
- code=$(echo "$resp" | grep -o '"code":[0-9]*' | grep -o '[0-9]*')
- fi
-
- if [ "$code" = "200" ]; then
- log_info "${filename} 上传成功"
- else
- log_error "${filename} 上传失败"
- echo " 响应: $resp"
- fi
-}
-
-# ── 主流程 ──
-echo "╔══════════════════════════════════════════════════════════════╗"
-echo "║ 上传到 OpenList 网盘 ║"
-echo "╚══════════════════════════════════════════════════════════════╝"
-echo ""
-echo " 服务地址: ${OPENLIST_URL}"
-echo " 上传模式: ${UPLOAD_MODE}"
-echo " 上传路径: ${UPLOAD_ROOT}/${UPLOAD_SUBDIR}/v${PKG_VERSION}"
-echo " 本地目录: ${DIST_DIR}"
-echo ""
-
-# 查找要上传的文件
-UPLOAD_FILES=""
-case "$UPLOAD_MODE" in
- offline)
- # 离线包: 仅 *_offline.run 文件
- UPLOAD_FILES=$(find "$DIST_DIR" -name "*_offline.run" 2>/dev/null)
- ;;
- online)
- # 在线包: .run (非 offline) + .ipk
- for f in "$DIST_DIR"/*.run "$DIST_DIR"/*.ipk; do
- [ -f "$f" ] || continue
- case "$(basename "$f")" in *_offline.run) continue ;; esac
- UPLOAD_FILES="$UPLOAD_FILES $f"
- done
- UPLOAD_FILES=$(echo "$UPLOAD_FILES" | sed 's/^ //')
- ;;
-esac
+API_URL="https://list.910501.xyz/api/packages"
+UPLOAD_FILES=$(find "$DIST_DIR" -type f -name "*.run" -o -name "*.ipk" 2>/dev/null)
if [ -z "$UPLOAD_FILES" ]; then
- echo "错误: 未找到可上传的文件"
- echo " 模式: $UPLOAD_MODE"
- echo " 目录: $DIST_DIR"
- exit 1
+ echo "错误: 在 $DIST_DIR 中没有找到 .run 或 .ipk 文件"
+ exit 1
fi
-# 获取 token
-TOKEN=$(get_token)
+echo "找到以下文件准备上传:"
+echo "$UPLOAD_FILES"
-# 创建远程目录
-REMOTE_DIR="${UPLOAD_ROOT}/${UPLOAD_SUBDIR}/v${PKG_VERSION}"
-echo ""
-echo "创建远程目录: ${REMOTE_DIR}"
-create_remote_dir "$TOKEN" "$REMOTE_DIR"
-
-# 上传文件
-echo ""
-echo "开始上传..."
-UPLOAD_COUNT=0
-for f in $UPLOAD_FILES; do
- upload_file "$TOKEN" "$f" "$REMOTE_DIR"
- UPLOAD_COUNT=$((UPLOAD_COUNT + 1))
+for file in $UPLOAD_FILES; do
+ filename=$(basename "$file")
+ echo "--------------------------------------------------"
+ echo "正在上传: $filename"
+
+ RESPONSE=$(curl -s -X POST "$API_URL" \
+ -H "Authorization: Bearer $OPENLIST_TOKEN" \
+ -F "file=@$file")
+
+ HTTP_CODE=$(echo "$RESPONSE" | grep -o 'HTTP/1.1 [0-9]*' | awk '{print $2}')
+ if echo "$RESPONSE" | grep -q '"success":true' || echo "$RESPONSE" | grep -q 'ok'; then
+ echo "✅ 上传成功: $filename"
+ else
+ echo "❌ 上传失败: $filename"
+ echo "返回内容: $RESPONSE"
+ exit 1
+ fi
done
-echo ""
-echo "╔══════════════════════════════════════════════════════════════╗"
-printf "║ ✅ 上传完成!共 %-2s 个文件 ║\n" "$UPLOAD_COUNT"
-echo "╚══════════════════════════════════════════════════════════════╝"
-echo ""
-echo "下载地址: ${OPENLIST_URL}${REMOTE_DIR}"
+echo "=================================================="
+echo "🎉 所有文件已成功上传至 OpenList!"