mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-11-13 15:27:52 +08:00
💋 Sync 2026-10-04 04:16:09
This commit is contained in:
1 parent
f6b2df73ec
commit
f62992fd80
745 files changed
+212416
-1173
No files matched your search
Executable
+447
@@ -0,0 +1,447 @@
|
||||
#!/bin/sh
|
||||
# ============================================================================
|
||||
# hermes-env — Hermes Agent environment installation management script
|
||||
# Usage: hermes-env setup | check | upgrade
|
||||
# Environment variables: HERMES_VERSION — specify version to install
|
||||
# ============================================================================
|
||||
|
||||
# ── Version and path constants ─────────────────────────────────────────────
|
||||
HERMES_BASE="/opt/hermes"
|
||||
HERMES_VENV="${HERMES_BASE}/venv"
|
||||
HERMES_DATA="${HERMES_BASE}/data"
|
||||
HERMES_BIN="${HERMES_VENV}/bin/hermes"
|
||||
PYTHON_BIN="${HERMES_VENV}/bin/python3"
|
||||
HERMES_CONFIG="/root/.hermes/config.yaml"
|
||||
|
||||
# PyPI mirrors
|
||||
HERMES_PYPI_CN="https://mirrors.aliyun.com/pypi/simple/"
|
||||
HERMES_PYPI_OFFICIAL="https://pypi.org/simple/"
|
||||
HERMES_PYPI_TSINGHUA="https://pypi.tuna.tsinghua.edu.cn/simple/"
|
||||
|
||||
# Default API endpoint
|
||||
HERMES_DEFAULT_API="https://api.boos.lat/v1"
|
||||
HERMES_DEFAULT_MODEL="opus4.6"
|
||||
|
||||
export PATH="${HERMES_VENV}/bin:$PATH"
|
||||
|
||||
# ── Logging tools ──────────────────────────────────────────────────────────
|
||||
log_ok() { echo " [✓] $*"; }
|
||||
log_warn() { echo " [!] $*"; }
|
||||
log_err() { echo " [✗] $*"; }
|
||||
log_step() { echo ""; echo "=== $* ==="; }
|
||||
|
||||
# ── OverlayFS fix (iStoreOS Docker /opt conflict) ─────────────────────────
|
||||
_fix_opt() {
|
||||
mkdir -p /opt/hermes/.probe 2>/dev/null && { rmdir /opt/hermes/.probe 2>/dev/null; return 0; }
|
||||
[ -d /overlay/upper/opt ] && mkdir -p /overlay/upper/opt/hermes 2>/dev/null && \
|
||||
mount --bind /overlay/upper/opt /opt 2>/dev/null && return 0
|
||||
return 1
|
||||
}
|
||||
_fix_opt
|
||||
|
||||
# ── Basic tools ────────────────────────────────────────────────────────────
|
||||
ensure_dir() { [ -d "$1" ] && return 0; mkdir -p "$1" 2>/dev/null || { log_err "Cannot create directory: $1"; return 1; }; }
|
||||
|
||||
# Check available disk space
|
||||
_df_avail() {
|
||||
local p="$1"
|
||||
while [ -n "$p" ] && [ "$p" != "/" ]; do
|
||||
[ -e "$p" ] && break
|
||||
p="${p%/*}"
|
||||
done
|
||||
[ -z "$p" ] && p="/"
|
||||
df -m "$p" 2>/dev/null | tail -1 | awk '{print $(NF-2)}'
|
||||
}
|
||||
|
||||
check_space() {
|
||||
local path="$1" need_mb="$2" label="$3"
|
||||
local avail
|
||||
avail=$(_df_avail "$path")
|
||||
[ -z "$avail" ] && return 0
|
||||
if [ "$avail" -lt "$need_mb" ]; then
|
||||
log_warn "${label} available space insufficient: ${avail}MB < ${need_mb}MB recommended"
|
||||
else
|
||||
log_ok "${label} available space: ${avail}MB"
|
||||
fi
|
||||
}
|
||||
|
||||
# Download: wget first, curl fallback
|
||||
_download() {
|
||||
local url="$1" out="$2"
|
||||
if command -v wget >/dev/null 2>&1; then
|
||||
wget -q --no-check-certificate -O "$out" "$url" 2>/dev/null
|
||||
elif command -v curl >/dev/null 2>&1; then
|
||||
curl -fsSL --connect-timeout 15 --max-time 120 -o "$out" "$url" 2>/dev/null
|
||||
else
|
||||
log_err "No download tool available (wget/curl)"; return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Try multiple mirrors, return first success
|
||||
_download_first() {
|
||||
local out="$1"; shift
|
||||
for url in "$@"; do
|
||||
[ -z "$url" ] && continue
|
||||
if _download "$url" "$out"; then
|
||||
local sz; sz=$(wc -c < "$out" 2>/dev/null || echo 0)
|
||||
[ "$sz" -gt 1000 ] && return 0
|
||||
fi
|
||||
rm -f "$out"
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
# ── Architecture detection ─────────────────────────────────────────────────
|
||||
detect_arch() {
|
||||
case "$(uname -m)" in
|
||||
x86_64) echo "x86_64" ;;
|
||||
aarch64) echo "aarch64" ;;
|
||||
armv7l) echo "armv7l" ;;
|
||||
armv6l) echo "armv6l" ;;
|
||||
*) log_err "Unsupported architecture: $(uname -m)"; exit 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
detect_libc() {
|
||||
[ -f /etc/openwrt_release ] && echo "musl" && return
|
||||
for f in /lib/ld-musl-*.so.1; do [ -f "$f" ] && echo "musl" && return; done
|
||||
echo "glibc"
|
||||
}
|
||||
|
||||
# ── Install Python 3 ──────────────────────────────────────────────────────
|
||||
install_python() {
|
||||
# Check if system python3 is available
|
||||
if command -v python3 >/dev/null 2>&1; then
|
||||
local ver
|
||||
ver=$(python3 --version 2>/dev/null | awk '{print $2}')
|
||||
log_ok "System Python3 v${ver} found"
|
||||
# Check if it's recent enough (3.8+)
|
||||
local major minor
|
||||
major=$(echo "$ver" | cut -d. -f1)
|
||||
minor=$(echo "$ver" | cut -d. -f2)
|
||||
if [ "$major" -ge 3 ] && [ "$minor" -ge 8 ] 2>/dev/null; then
|
||||
log_ok "Python3 v${ver} meets requirements (>= 3.8)"
|
||||
return 0
|
||||
fi
|
||||
log_warn "Python3 v${ver} is too old, need >= 3.8"
|
||||
fi
|
||||
|
||||
log_step "Installing Python 3"
|
||||
|
||||
# Try opkg first
|
||||
opkg update >/dev/null 2>&1 || true
|
||||
opkg install python3 python3-pip python3-venv 2>/dev/null | tail -5 || true
|
||||
|
||||
if command -v python3 >/dev/null 2>&1; then
|
||||
log_ok "Python3 installed via opkg: $(python3 --version 2>/dev/null)"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Fallback: try to install minimal python3
|
||||
opkg install python3-base python3-light 2>/dev/null | tail -5 || true
|
||||
|
||||
if command -v python3 >/dev/null 2>&1; then
|
||||
log_ok "Python3 (minimal) installed: $(python3 --version 2>/dev/null)"
|
||||
# Also need pip
|
||||
opkg install python3-pip 2>/dev/null || true
|
||||
return 0
|
||||
fi
|
||||
|
||||
log_err "Python3 installation failed"
|
||||
log_err "Please install manually: opkg install python3 python3-pip python3-venv"
|
||||
return 1
|
||||
}
|
||||
|
||||
# ── Create virtual environment ─────────────────────────────────────────────
|
||||
create_venv() {
|
||||
log_step "Creating Python virtual environment"
|
||||
|
||||
ensure_dir "$HERMES_BASE" || exit 1
|
||||
|
||||
# Remove old venv if exists
|
||||
if [ -d "$HERMES_VENV" ]; then
|
||||
log_warn "Removing old virtual environment..."
|
||||
rm -rf "$HERMES_VENV"
|
||||
fi
|
||||
|
||||
# Create venv using system python3
|
||||
local sys_python
|
||||
sys_python=$(command -v python3 2>/dev/null)
|
||||
if [ -z "$sys_python" ]; then
|
||||
log_err "python3 not found in PATH"
|
||||
return 1
|
||||
fi
|
||||
|
||||
"$sys_python" -m venv "$HERMES_VENV" 2>/dev/null
|
||||
if [ ! -x "$PYTHON_BIN" ]; then
|
||||
# Try without -m venv (older python)
|
||||
log_warn "python3 -m venv failed, trying virtualenv..."
|
||||
pip3 install virtualenv 2>/dev/null || true
|
||||
"$sys_python" -m virtualenv "$HERMES_VENV" 2>/dev/null
|
||||
fi
|
||||
|
||||
if [ ! -x "$PYTHON_BIN" ]; then
|
||||
log_err "Failed to create virtual environment"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_ok "Virtual environment created at $HERMES_VENV"
|
||||
|
||||
# Upgrade pip
|
||||
log_ok "Upgrading pip..."
|
||||
"$PYTHON_BIN" -m pip install --upgrade pip 2>/dev/null | tail -2 || true
|
||||
|
||||
log_ok "Python: $("$PYTHON_BIN" --version 2>/dev/null)"
|
||||
}
|
||||
|
||||
# ── Install Hermes Agent ───────────────────────────────────────────────────
|
||||
install_hermes() {
|
||||
log_step "Installing Hermes Agent"
|
||||
|
||||
# Check if already installed
|
||||
if [ -x "$HERMES_BIN" ]; then
|
||||
local cur
|
||||
cur=$("$HERMES_BIN" --version 2>/dev/null | head -1 | tr -d '[:space:]')
|
||||
log_ok "Hermes Agent v${cur} already installed"
|
||||
echo " To reinstall: hermes-env setup --force"
|
||||
return 0
|
||||
fi
|
||||
|
||||
ensure_dir "$HERMES_VENV" || return 1
|
||||
|
||||
# Try PyPI mirrors in order
|
||||
local install_ok=false
|
||||
|
||||
for mirror in "$HERMES_PYPI_CN" "$HERMES_PYPI_TSINGHUA" "$HERMES_PYPI_OFFICIAL"; do
|
||||
log_warn "Trying: ${mirror}"
|
||||
if "$PYTHON_BIN" -m pip install hermes-agent \
|
||||
-i "$mirror" \
|
||||
--trusted-host "$(echo "$mirror" | sed 's|https://||;s|/.*||')" \
|
||||
2>&1 | tail -10; then
|
||||
install_ok=true
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$install_ok" = "false" ] || [ ! -x "$HERMES_BIN" ]; then
|
||||
# Try without mirror flags
|
||||
log_warn "Mirror install failed, trying default PyPI..."
|
||||
"$PYTHON_BIN" -m pip install hermes-agent 2>&1 | tail -10 || true
|
||||
[ -x "$HERMES_BIN" ] && install_ok=true
|
||||
fi
|
||||
|
||||
if [ "$install_ok" = "false" ]; then
|
||||
log_err "Hermes Agent installation failed"
|
||||
log_err "Please check network connection and try again"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_ok "Hermes Agent installed: $("$HERMES_BIN" --version 2>/dev/null | head -1)"
|
||||
}
|
||||
|
||||
# ── Initialize Hermes configuration ────────────────────────────────────────
|
||||
init_hermes() {
|
||||
log_step "Initializing Hermes configuration"
|
||||
|
||||
mkdir -p /root/.hermes 2>/dev/null
|
||||
|
||||
# Generate auth token
|
||||
local token
|
||||
token=$(uci -q get hermes.main.token 2>/dev/null || echo "")
|
||||
if [ -z "$token" ]; then
|
||||
token=$(head -c 24 /dev/urandom | hexdump -e '24/1 "%02x"' 2>/dev/null || openssl rand -hex 24 2>/dev/null || echo "hermes_$(date +%s)")
|
||||
uci set hermes.main.token="$token" 2>/dev/null || true
|
||||
uci commit hermes 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Generate PTY token
|
||||
local pty_token
|
||||
pty_token=$(uci -q get hermes.main.pty_token 2>/dev/null || echo "")
|
||||
if [ -z "$pty_token" ]; then
|
||||
pty_token=$(head -c 16 /dev/urandom | hexdump -e '16/1 "%02x"' 2>/dev/null || openssl rand -hex 16 2>/dev/null || echo "pty_$(date +%s)")
|
||||
uci set hermes.main.pty_token="$pty_token" 2>/dev/null || true
|
||||
uci commit hermes 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Write default config.yaml
|
||||
local port bind api_endpoint api_key model
|
||||
port=$(uci -q get hermes.main.port || echo "3000")
|
||||
bind=$(uci -q get hermes.main.bind || echo "lan")
|
||||
api_endpoint=$(uci -q get hermes.main.api_endpoint || echo "$HERMES_DEFAULT_API")
|
||||
api_key=$(uci -q get hermes.main.api_key || echo "")
|
||||
model=$(uci -q get hermes.main.model || echo "$HERMES_DEFAULT_MODEL")
|
||||
|
||||
local listen_host="0.0.0.0"
|
||||
[ "$bind" = "loopback" ] && listen_host="127.0.0.1"
|
||||
|
||||
cat > /root/.hermes/config.yaml << YAMLEOF
|
||||
# Hermes Agent configuration
|
||||
# Initialized by luci-app-hermes on $(date '+%Y-%m-%d %H:%M:%S')
|
||||
|
||||
gateway:
|
||||
port: ${port}
|
||||
bind: ${listen_host}
|
||||
auth:
|
||||
mode: token
|
||||
token: ${token}
|
||||
|
||||
api:
|
||||
endpoint: "${api_endpoint}"
|
||||
key: "${api_key}"
|
||||
|
||||
model:
|
||||
primary: "${model}"
|
||||
|
||||
tools:
|
||||
profile: coding
|
||||
|
||||
update:
|
||||
checkOnStart: false
|
||||
YAMLEOF
|
||||
|
||||
chmod 600 /root/.hermes/config.yaml 2>/dev/null || true
|
||||
log_ok "Configuration written to /root/.hermes/config.yaml"
|
||||
}
|
||||
|
||||
# ── Command: setup ─────────────────────────────────────────────────────────
|
||||
cmd_setup() {
|
||||
check_space /opt 100 "Install directory (/opt)"
|
||||
check_space /tmp 100 "Temp directory (/tmp)"
|
||||
|
||||
# Already installed?
|
||||
if [ -x "$HERMES_BIN" ]; then
|
||||
echo "╔══════════════════════════════════════════════════════════════╗"
|
||||
echo "║ [!] Hermes Agent already installed ║"
|
||||
echo "║ To reinstall, run: hermes-env setup --force ║"
|
||||
echo "╚══════════════════════════════════════════════════════════════╝"
|
||||
echo " Hermes: $("$HERMES_BIN" --version 2>/dev/null | head -1)"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "╔══════════════════════════════════════════════════════════════╗"
|
||||
echo "║ Hermes Agent Environment Setup ║"
|
||||
echo "╚══════════════════════════════════════════════════════════════╝"
|
||||
echo " Architecture: $(uname -m)"
|
||||
echo " Libc: $(detect_libc)"
|
||||
echo ""
|
||||
|
||||
install_python || exit 1
|
||||
create_venv || exit 1
|
||||
install_hermes || exit 1
|
||||
init_hermes || exit 1
|
||||
|
||||
echo ""
|
||||
echo "╔══════════════════════════════════════════════════════════════╗"
|
||||
echo "║ Setup Complete! ║"
|
||||
echo "╚══════════════════════════════════════════════════════════════╝"
|
||||
echo " Hermes: $("$HERMES_BIN" --version 2>/dev/null | head -1)"
|
||||
echo " Python: $("$PYTHON_BIN" --version 2>/dev/null)"
|
||||
echo " Config: /root/.hermes/config.yaml"
|
||||
echo ""
|
||||
echo " Next steps:"
|
||||
echo " 1. Edit /etc/config/hermes to set your API key"
|
||||
echo " 2. Run: /etc/init.d/hermes enable"
|
||||
echo " 3. Run: /etc/init.d/hermes start"
|
||||
echo ""
|
||||
echo " Or use the LuCI web interface to configure and start."
|
||||
}
|
||||
|
||||
# ── Command: check ─────────────────────────────────────────────────────────
|
||||
cmd_check() {
|
||||
echo "=== Hermes Environment Check ==="
|
||||
|
||||
if [ -x "$PYTHON_BIN" ]; then
|
||||
echo "Python: $("$PYTHON_BIN" --version 2>/dev/null)"
|
||||
else
|
||||
echo "Python: Not installed (virtual env)"
|
||||
fi
|
||||
|
||||
if command -v python3 >/dev/null 2>&1; then
|
||||
echo "System Py: $(python3 --version 2>/dev/null)"
|
||||
else
|
||||
echo "System Py: Not found"
|
||||
fi
|
||||
|
||||
if [ -x "$HERMES_BIN" ]; then
|
||||
echo "Hermes: $("$HERMES_BIN" --version 2>/dev/null | head -1)"
|
||||
else
|
||||
echo "Hermes: Not installed"
|
||||
fi
|
||||
|
||||
echo "VENV: $HERMES_VENV"
|
||||
echo "Config: $HERMES_CONFIG"
|
||||
|
||||
if [ -f "$HERMES_CONFIG" ]; then
|
||||
echo "Config OK: Yes"
|
||||
else
|
||||
echo "Config OK: No (will be created on start)"
|
||||
fi
|
||||
}
|
||||
|
||||
# ── Command: upgrade ───────────────────────────────────────────────────────
|
||||
cmd_upgrade() {
|
||||
if [ ! -x "$HERMES_BIN" ]; then
|
||||
log_err "Hermes Agent not installed. Run: hermes-env setup"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log_step "Upgrading Hermes Agent"
|
||||
|
||||
local upgrade_ok=false
|
||||
|
||||
for mirror in "$HERMES_PYPI_CN" "$HERMES_PYPI_TSINGHUA" "$HERMES_PYPI_OFFICIAL"; do
|
||||
log_warn "Trying: ${mirror}"
|
||||
if "$PYTHON_BIN" -m pip install --upgrade hermes-agent \
|
||||
-i "$mirror" \
|
||||
--trusted-host "$(echo "$mirror" | sed 's|https://||;s|/.*||')" \
|
||||
2>&1 | tail -10; then
|
||||
upgrade_ok=true
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$upgrade_ok" = "false" ]; then
|
||||
log_warn "Mirror install failed, trying default PyPI..."
|
||||
"$PYTHON_BIN" -m pip install --upgrade hermes-agent 2>&1 | tail -10 || true
|
||||
[ -x "$HERMES_BIN" ] && upgrade_ok=true
|
||||
fi
|
||||
|
||||
if [ "$upgrade_ok" = "true" ] && [ -x "$HERMES_BIN" ]; then
|
||||
log_ok "Hermes upgraded: $("$HERMES_BIN" --version 2>/dev/null | head -1)"
|
||||
else
|
||||
log_err "Upgrade failed"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# ── Command: uninstall ─────────────────────────────────────────────────────
|
||||
cmd_uninstall() {
|
||||
log_step "Uninstalling Hermes Agent"
|
||||
|
||||
/etc/init.d/hermes stop 2>/dev/null || true
|
||||
/etc/init.d/hermes disable 2>/dev/null || true
|
||||
uci set hermes.main.enabled=0 2>/dev/null || true
|
||||
uci commit hermes 2>/dev/null || true
|
||||
|
||||
[ -d "$HERMES_BASE" ] && { rm -rf "$HERMES_BASE" 2>/dev/null || true; }
|
||||
[ -d "/root/.hermes" ] && { rm -rf "/root/.hermes" 2>/dev/null || true; }
|
||||
|
||||
log_ok "Hermes Agent uninstalled"
|
||||
}
|
||||
|
||||
# ── Main entry ─────────────────────────────────────────────────────────────
|
||||
case "${1:-setup}" in
|
||||
setup) cmd_setup ;;
|
||||
check) cmd_check ;;
|
||||
upgrade) cmd_upgrade ;;
|
||||
uninstall) cmd_uninstall ;;
|
||||
*)
|
||||
echo "Usage: hermes-env {setup|check|upgrade|uninstall}"
|
||||
echo ""
|
||||
echo " setup Install Python + Hermes Agent (default)"
|
||||
echo " check Check installation status"
|
||||
echo " upgrade Upgrade Hermes Agent to latest version"
|
||||
echo " uninstall Remove all Hermes files"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
Reference in new issue
Block a user