mirror of
https://github.com/hotwa/luci-app-openclaw.git
synced 2026-03-31 04:52:33 +00:00
release: v1.0.0 — LuCI 管理界面、一键安装、12+ AI 模型提供商
This commit is contained in:
143
scripts/build_ipk.sh
Executable file
143
scripts/build_ipk.sh
Executable file
@@ -0,0 +1,143 @@
|
||||
#!/bin/sh
|
||||
# ============================================================================
|
||||
# 本地构建 .ipk 包 (无需 OpenWrt SDK)
|
||||
# 用法: sh scripts/build_ipk.sh [output_dir]
|
||||
# ============================================================================
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
|
||||
PKG_DIR=$(cd "$SCRIPT_DIR/.." && pwd)
|
||||
OUT_DIR="${1:-$PKG_DIR/dist}"
|
||||
# 确保 OUT_DIR 是绝对路径
|
||||
case "$OUT_DIR" in
|
||||
/*) ;;
|
||||
*) OUT_DIR="$PKG_DIR/$OUT_DIR" ;;
|
||||
esac
|
||||
mkdir -p "$OUT_DIR"
|
||||
PKG_NAME="luci-app-openclaw"
|
||||
PKG_VERSION=$(cat "$PKG_DIR/VERSION" 2>/dev/null | tr -d '[:space:]' || echo "1.0.0")
|
||||
PKG_RELEASE="1"
|
||||
|
||||
echo "=== 构建 ${PKG_NAME} .ipk 包 ==="
|
||||
|
||||
STAGING=$(mktemp -d)
|
||||
trap "rm -rf '$STAGING'" EXIT
|
||||
|
||||
# ── 构建 data.tar.gz ──
|
||||
DATA_DIR="$STAGING/data"
|
||||
mkdir -p "$DATA_DIR"
|
||||
|
||||
# UCI config
|
||||
mkdir -p "$DATA_DIR/etc/config"
|
||||
cp "$PKG_DIR/root/etc/config/openclaw" "$DATA_DIR/etc/config/"
|
||||
|
||||
# UCI defaults
|
||||
mkdir -p "$DATA_DIR/etc/uci-defaults"
|
||||
cp "$PKG_DIR/root/etc/uci-defaults/99-openclaw" "$DATA_DIR/etc/uci-defaults/"
|
||||
chmod +x "$DATA_DIR/etc/uci-defaults/99-openclaw"
|
||||
|
||||
# init.d
|
||||
mkdir -p "$DATA_DIR/etc/init.d"
|
||||
cp "$PKG_DIR/root/etc/init.d/openclaw" "$DATA_DIR/etc/init.d/"
|
||||
chmod +x "$DATA_DIR/etc/init.d/openclaw"
|
||||
|
||||
# bin
|
||||
mkdir -p "$DATA_DIR/usr/bin"
|
||||
cp "$PKG_DIR/root/usr/bin/openclaw-env" "$DATA_DIR/usr/bin/"
|
||||
chmod +x "$DATA_DIR/usr/bin/openclaw-env"
|
||||
|
||||
# LuCI controller
|
||||
mkdir -p "$DATA_DIR/usr/lib/lua/luci/controller"
|
||||
cp "$PKG_DIR/luasrc/controller/openclaw.lua" "$DATA_DIR/usr/lib/lua/luci/controller/"
|
||||
|
||||
# LuCI CBI
|
||||
mkdir -p "$DATA_DIR/usr/lib/lua/luci/model/cbi/openclaw"
|
||||
cp "$PKG_DIR/luasrc/model/cbi/openclaw/"*.lua "$DATA_DIR/usr/lib/lua/luci/model/cbi/openclaw/"
|
||||
|
||||
# LuCI views
|
||||
mkdir -p "$DATA_DIR/usr/lib/lua/luci/view/openclaw"
|
||||
cp "$PKG_DIR/luasrc/view/openclaw/"*.htm "$DATA_DIR/usr/lib/lua/luci/view/openclaw/"
|
||||
|
||||
# oc-config assets
|
||||
mkdir -p "$DATA_DIR/usr/share/openclaw"
|
||||
cp "$PKG_DIR/root/usr/share/openclaw/oc-config.sh" "$DATA_DIR/usr/share/openclaw/"
|
||||
chmod +x "$DATA_DIR/usr/share/openclaw/oc-config.sh"
|
||||
cp "$PKG_DIR/root/usr/share/openclaw/web-pty.js" "$DATA_DIR/usr/share/openclaw/"
|
||||
|
||||
# Web PTY UI
|
||||
cp -r "$PKG_DIR/root/usr/share/openclaw/ui" "$DATA_DIR/usr/share/openclaw/"
|
||||
|
||||
# i18n (po2lmo 可选)
|
||||
mkdir -p "$DATA_DIR/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" "$DATA_DIR/usr/lib/lua/luci/i18n/openclaw.zh-cn.lmo" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# 计算安装大小
|
||||
INSTALLED_SIZE=$(du -sk "$DATA_DIR" | awk '{print $1}')
|
||||
|
||||
(cd "$DATA_DIR" && tar czf "$STAGING/data.tar.gz" .)
|
||||
|
||||
# ── 构建 control.tar.gz ──
|
||||
CTRL_DIR="$STAGING/control"
|
||||
mkdir -p "$CTRL_DIR"
|
||||
|
||||
cat > "$CTRL_DIR/control" << EOF
|
||||
Package: ${PKG_NAME}
|
||||
Version: ${PKG_VERSION}-${PKG_RELEASE}
|
||||
Depends: luci-compat, luci-base, curl, openssl-util
|
||||
Source: https://github.com/10000ge10000/luci-app-openclaw
|
||||
SourceName: ${PKG_NAME}
|
||||
License: GPL-3.0
|
||||
Section: luci
|
||||
SourceDateEpoch: $(date +%s)
|
||||
Maintainer: 10000ge10000 <10000ge10000@users.noreply.github.com>
|
||||
Architecture: all
|
||||
Installed-Size: ${INSTALLED_SIZE}
|
||||
Description: OpenClaw AI 网关 LuCI 管理插件
|
||||
EOF
|
||||
|
||||
cat > "$CTRL_DIR/postinst" << 'EOF'
|
||||
#!/bin/sh
|
||||
[ -n "${IPKG_INSTROOT}" ] || {
|
||||
( . /etc/uci-defaults/99-openclaw ) && rm -f /etc/uci-defaults/99-openclaw
|
||||
rm -f /tmp/luci-indexcache /tmp/luci-modulecache/* 2>/dev/null
|
||||
exit 0
|
||||
}
|
||||
EOF
|
||||
chmod +x "$CTRL_DIR/postinst"
|
||||
|
||||
cat > "$CTRL_DIR/postrm" << 'EOF'
|
||||
#!/bin/sh
|
||||
[ -n "${IPKG_INSTROOT}" ] || {
|
||||
rm -f /tmp/luci-indexcache /tmp/luci-modulecache/* 2>/dev/null
|
||||
}
|
||||
EOF
|
||||
chmod +x "$CTRL_DIR/postrm"
|
||||
|
||||
cat > "$CTRL_DIR/conffiles" << 'EOF'
|
||||
/etc/config/openclaw
|
||||
EOF
|
||||
|
||||
(cd "$CTRL_DIR" && tar czf "$STAGING/control.tar.gz" .)
|
||||
|
||||
# ── 组装 .ipk (ar 格式) ──
|
||||
mkdir -p "$OUT_DIR"
|
||||
IPK_FILE="$OUT_DIR/${PKG_NAME}_${PKG_VERSION}-${PKG_RELEASE}_all.ipk"
|
||||
|
||||
echo "2.0" > "$STAGING/debian-binary"
|
||||
|
||||
# 清理旧文件
|
||||
rm -f "$IPK_FILE"
|
||||
|
||||
# 组装 .ipk — OpenWrt opkg 使用 tar.gz 格式 (非 Debian 的 ar 格式)
|
||||
(cd "$STAGING" && tar czf "$IPK_FILE" debian-binary control.tar.gz data.tar.gz)
|
||||
|
||||
IPK_SIZE=$(wc -c < "$IPK_FILE" | tr -d ' ')
|
||||
echo ""
|
||||
echo "=== 构建完成 ==="
|
||||
echo "输出文件: $IPK_FILE"
|
||||
echo "文件大小: ${IPK_SIZE} bytes"
|
||||
echo "安装大小: ${INSTALLED_SIZE} KB"
|
||||
echo ""
|
||||
echo "安装方法: opkg install ${PKG_NAME}_${PKG_VERSION}-${PKG_RELEASE}_all.ipk"
|
||||
235
scripts/build_run.sh
Executable file
235
scripts/build_run.sh
Executable file
@@ -0,0 +1,235 @@
|
||||
#!/bin/sh
|
||||
# ============================================================================
|
||||
# iStoreOS .run 自解压包构建脚本
|
||||
# 用法: sh scripts/build_run.sh [output_dir]
|
||||
# ============================================================================
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
|
||||
PKG_DIR=$(cd "$SCRIPT_DIR/.." && pwd)
|
||||
OUT_DIR="${1:-$PKG_DIR/dist}"
|
||||
# 确保 OUT_DIR 是绝对路径
|
||||
case "$OUT_DIR" in
|
||||
/*) ;;
|
||||
*) OUT_DIR="$PKG_DIR/$OUT_DIR" ;;
|
||||
esac
|
||||
mkdir -p "$OUT_DIR"
|
||||
PKG_NAME="luci-app-openclaw"
|
||||
PKG_VERSION=$(cat "$PKG_DIR/VERSION" 2>/dev/null | tr -d '[:space:]' || echo "1.0.0")
|
||||
|
||||
echo "=== 构建 iStoreOS .run 安装包 ==="
|
||||
echo "源目录: $PKG_DIR"
|
||||
echo "输出到: $OUT_DIR"
|
||||
|
||||
# 创建临时打包目录
|
||||
STAGING=$(mktemp -d)
|
||||
trap "rm -rf '$STAGING'" EXIT
|
||||
|
||||
# 安装文件到暂存区
|
||||
install_files() {
|
||||
local dest="$1"
|
||||
|
||||
# UCI config
|
||||
mkdir -p "$dest/etc/config"
|
||||
cp "$PKG_DIR/root/etc/config/openclaw" "$dest/etc/config/"
|
||||
|
||||
# UCI defaults
|
||||
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"
|
||||
|
||||
# init.d
|
||||
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"
|
||||
|
||||
# bin
|
||||
mkdir -p "$dest/usr/bin"
|
||||
cp "$PKG_DIR/root/usr/bin/openclaw-env" "$dest/usr/bin/"
|
||||
chmod +x "$dest/usr/bin/openclaw-env"
|
||||
|
||||
# LuCI controller
|
||||
mkdir -p "$dest/usr/lib/lua/luci/controller"
|
||||
cp "$PKG_DIR/luasrc/controller/openclaw.lua" "$dest/usr/lib/lua/luci/controller/"
|
||||
|
||||
# LuCI CBI
|
||||
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/"
|
||||
|
||||
# LuCI views
|
||||
mkdir -p "$dest/usr/lib/lua/luci/view/openclaw"
|
||||
cp "$PKG_DIR/luasrc/view/openclaw/"*.htm "$dest/usr/lib/lua/luci/view/openclaw/"
|
||||
|
||||
# oc-config assets
|
||||
mkdir -p "$dest/usr/share/openclaw"
|
||||
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/"
|
||||
|
||||
# Web PTY UI (recursive copy)
|
||||
cp -r "$PKG_DIR/root/usr/share/openclaw/ui" "$dest/usr/share/openclaw/"
|
||||
}
|
||||
|
||||
# 创建安装器脚本头部
|
||||
create_installer() {
|
||||
cat > "$STAGING/install.sh" << 'INSTALLER_EOF'
|
||||
#!/bin/sh
|
||||
# luci-app-openclaw iStoreOS 安装器
|
||||
set -e
|
||||
|
||||
echo "╔══════════════════════════════════════════════════════════════╗"
|
||||
echo "║ luci-app-openclaw — OpenClaw AI Gateway 插件 ║"
|
||||
echo "╚══════════════════════════════════════════════════════════════╝"
|
||||
echo ""
|
||||
|
||||
# 检查系统
|
||||
if [ ! -f /etc/openwrt_release ]; then
|
||||
echo "错误: 此安装包仅适用于 OpenWrt/iStoreOS 系统"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 检查架构
|
||||
ARCH=$(uname -m)
|
||||
case "$ARCH" in
|
||||
x86_64|aarch64) ;;
|
||||
*) echo "错误: 不支持的架构 $ARCH (仅支持 x86_64/aarch64)"; exit 1 ;;
|
||||
esac
|
||||
|
||||
# 检查依赖
|
||||
for dep in luci-compat luci-base; do
|
||||
if ! opkg list-installed 2>/dev/null | grep -q "^${dep} "; then
|
||||
echo "警告: 缺少依赖 $dep,尝试安装..."
|
||||
opkg update >/dev/null 2>&1 || true
|
||||
opkg install "$dep" 2>/dev/null || echo " 安装 $dep 失败,请手动安装"
|
||||
fi
|
||||
done
|
||||
|
||||
echo "正在安装文件..."
|
||||
|
||||
# 解压 payload (从 MARKER 行之后)
|
||||
ARCHIVE=$(awk '/^__ARCHIVE_BELOW__/ {print NR + 1; exit 0; }' "$0")
|
||||
tail -n +$ARCHIVE "$0" | tar xzf - -C / 2>/dev/null
|
||||
|
||||
# 注册到 opkg,使 iStore 和 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"
|
||||
|
||||
# 写入 control 文件
|
||||
cat > "$INFO_DIR/$PKG.control" << CTLEOF
|
||||
Package: $PKG
|
||||
Version: $PKG_VER
|
||||
Depends: luci-compat, luci-base, curl, openssl-util
|
||||
Section: luci
|
||||
Architecture: all
|
||||
Installed-Size: 0
|
||||
Description: OpenClaw AI Gateway — LuCI 界面
|
||||
CTLEOF
|
||||
|
||||
# 写入文件列表 (payload 中已安装的文件)
|
||||
cat > "$INFO_DIR/$PKG.list" << LISTEOF
|
||||
__FILE_LIST__
|
||||
LISTEOF
|
||||
|
||||
# 写入 prerm 脚本 (卸载前执行)
|
||||
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, curl, openssl-util
|
||||
Status: install user installed
|
||||
Architecture: all
|
||||
Conffiles:
|
||||
/etc/config/openclaw 0
|
||||
Installed-Time: $INSTALL_TIME
|
||||
STEOF
|
||||
|
||||
echo "已注册到 opkg (iStore 可管理)"
|
||||
|
||||
# 执行 uci-defaults
|
||||
if [ -f /etc/uci-defaults/99-openclaw ]; then
|
||||
echo "执行初始化脚本..."
|
||||
( . /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
|
||||
|
||||
echo ""
|
||||
echo "✅ 安装完成!"
|
||||
echo ""
|
||||
echo "后续步骤:"
|
||||
echo " 1. 运行 openclaw-env setup — 下载 Node.js 并安装 OpenClaw"
|
||||
echo " 2. 访问 LuCI → 服务 → OpenClaw 进行配置"
|
||||
echo " 3. 或执行 /etc/init.d/openclaw enable && /etc/init.d/openclaw start"
|
||||
echo ""
|
||||
|
||||
exit 0
|
||||
__ARCHIVE_BELOW__
|
||||
INSTALLER_EOF
|
||||
}
|
||||
|
||||
# 构建
|
||||
echo ""
|
||||
echo "[1/4] 安装文件到暂存区..."
|
||||
install_files "$STAGING/payload"
|
||||
|
||||
echo "[2/4] 生成文件列表..."
|
||||
# 生成安装文件列表 (供 opkg 卸载时使用)
|
||||
FILE_LIST=$(cd "$STAGING/payload" && find . -type f | sed 's|^\./|/|' | sort)
|
||||
echo " 共 $(echo "$FILE_LIST" | wc -l | tr -d ' ') 个文件"
|
||||
|
||||
echo "[3/4] 创建安装器..."
|
||||
create_installer
|
||||
|
||||
# 替换安装器中的占位符
|
||||
sed -i "s|__PKG_VERSION__|${PKG_VERSION}|g" "$STAGING/install.sh"
|
||||
# 替换文件列表占位符 — 使用临时文件拼接避免 sed/awk 多行问题
|
||||
{
|
||||
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"
|
||||
|
||||
echo "[4/4] 打包..."
|
||||
mkdir -p "$OUT_DIR"
|
||||
|
||||
# 创建 payload tarball
|
||||
(cd "$STAGING/payload" && tar czf "$STAGING/payload.tar.gz" .)
|
||||
|
||||
# 组合: installer header + payload
|
||||
RUN_FILE="$OUT_DIR/${PKG_NAME}_${PKG_VERSION}.run"
|
||||
cat "$STAGING/install.sh" "$STAGING/payload.tar.gz" > "$RUN_FILE"
|
||||
chmod +x "$RUN_FILE"
|
||||
|
||||
FILE_SIZE=$(wc -c < "$RUN_FILE" | tr -d ' ')
|
||||
echo ""
|
||||
echo "=== 构建完成 ==="
|
||||
echo "输出文件: $RUN_FILE"
|
||||
echo "文件大小: $FILE_SIZE bytes"
|
||||
echo ""
|
||||
echo "安装方法: sh ${PKG_NAME}_${PKG_VERSION}.run"
|
||||
Reference in New Issue
Block a user