mirror of
https://github.com/TencentCloud/TencentDB-Agent-Memory
synced 2026-07-10 20:34:30 +00:00
369 lines
14 KiB
Bash
Executable File
369 lines
14 KiB
Bash
Executable File
#!/bin/bash
|
||
#
|
||
# install_memory_tencentdb.sh
|
||
#
|
||
# 在 install_hermes_ubuntu.sh 之后执行,用于:
|
||
# 1. 通过 npm 下载 @tencentdb-agent-memory/memory-tencentdb@latest 到
|
||
# $MEMORY_TENCENTDB_ROOT/tdai-memory-openclaw-plugin(默认 ~/.memory-tencentdb/tdai-memory-openclaw-plugin)
|
||
# 2. 安装 Gateway 的 Node.js 依赖(npm install)
|
||
# 3. 配置 hermes config.yaml 使用 memory_tencentdb 记忆提供者
|
||
# 4. 设置 Gateway 自动启动环境变量
|
||
#
|
||
# 路径约定(全部位于 ~/.memory-tencentdb/ 之下,可通过环境变量覆盖):
|
||
# $MEMORY_TENCENTDB_ROOT 默认 ~/.memory-tencentdb
|
||
# $TDAI_INSTALL_DIR 默认 $MEMORY_TENCENTDB_ROOT/tdai-memory-openclaw-plugin
|
||
# $TDAI_DATA_DIR 默认 $MEMORY_TENCENTDB_ROOT/memory-tdai
|
||
#
|
||
# 旧版本(<= 0.3.x)使用 ~/tdai-memory-openclaw-plugin 与 ~/memory-tdai;
|
||
# 本脚本会在执行前自动迁移这两个旧目录到新位置(见 Step 0)。
|
||
#
|
||
# 使用方式:
|
||
# 以目标用户身份执行(推荐):
|
||
# su - <username> -c "bash ~/install_memory_tencentdb.sh"
|
||
# # 或直接以该用户登录后执行
|
||
# bash ~/install_memory_tencentdb.sh
|
||
#
|
||
# 以 root 身份执行(镜像构建场景):
|
||
# bash ~/install_memory_tencentdb.sh
|
||
# # root 会自动 su 切换到目标用户执行,完成后修复权限
|
||
#
|
||
# 前置条件:
|
||
# - install_hermes_ubuntu.sh 已执行完成(hermes-agent 已安装)
|
||
# - Node.js >= 22 已安装
|
||
|
||
set -e
|
||
|
||
# 动态获取目标安装用户及其 HOME 目录。
|
||
# 优先级:
|
||
# 1. 显式 ``INSTALL_AS_USER`` 环境变量(管理员脚本场景:root 跑安装但
|
||
# 想为另一个用户配置)
|
||
# 2. ``SUDO_USER``(被 ``sudo`` 调用时,切回原用户而不是 root)
|
||
# 3. ``whoami`` —— 当前 EUID 对应的用户
|
||
#
|
||
# 注意:当 root 直接 ssh 登录跑(非 sudo)时,前两个都不会被设置,
|
||
# ``whoami`` 返回 ``root``。下面的 ``id -u`` == 0 分支会识别这种"目标
|
||
# 就是 root"的情况、跳过 ``su - root`` 递归。
|
||
USERNAME="${INSTALL_AS_USER:-${SUDO_USER:-$(whoami)}}"
|
||
USER_HOME=$(eval echo ~$USERNAME)
|
||
|
||
# npm 包名
|
||
NPM_PACKAGE="@tencentdb-agent-memory/memory-tencentdb@latest"
|
||
|
||
# Hermes 路径
|
||
HERMES_HOME="$USER_HOME/.hermes"
|
||
# HERMES_AGENT_DIR(fix: issue #18)
|
||
# 用户通过环境变量传什么就用什么;未设置时 fallback 到传统路径。
|
||
# 如果目录不存在,后续前置检查会统一报错。
|
||
HERMES_AGENT_DIR="${HERMES_AGENT_DIR:-$HERMES_HOME/hermes-agent}"
|
||
HERMES_CONFIG="$HERMES_HOME/config.yaml"
|
||
|
||
# memory-tencentdb 统一根目录(所有 tdai 相关数据/代码都收纳在此)
|
||
# 可通过环境变量 MEMORY_TENCENTDB_ROOT 覆盖
|
||
MEMORY_TENCENTDB_ROOT="${MEMORY_TENCENTDB_ROOT:-$USER_HOME/.memory-tencentdb}"
|
||
|
||
# tdai 解压目标目录(位于统一根目录下)
|
||
# 可通过环境变量 TDAI_INSTALL_DIR 覆盖
|
||
TDAI_INSTALL_DIR="${TDAI_INSTALL_DIR:-$MEMORY_TENCENTDB_ROOT/tdai-memory-openclaw-plugin}"
|
||
|
||
# tdai 数据目录(Gateway baseDir,位于统一根目录下)
|
||
# 可通过环境变量 TDAI_DATA_DIR 覆盖
|
||
TDAI_DATA_DIR="${TDAI_DATA_DIR:-$MEMORY_TENCENTDB_ROOT/memory-tdai}"
|
||
|
||
# 旧路径(仅用于自动迁移)
|
||
LEGACY_INSTALL_DIR="$USER_HOME/tdai-memory-openclaw-plugin"
|
||
LEGACY_DATA_DIR="$USER_HOME/memory-tdai"
|
||
|
||
# ==================== root → 自动切换到目标用户 ====================
|
||
# 与 install_hermes_ubuntu.sh 保持一致:如果以 root 执行且目标用户不是
|
||
# root,自动 su 切到目标用户运行实际安装逻辑。
|
||
#
|
||
# 如果当前是 root 且目标用户也是 root(``USERNAME=root``,例如直接 ssh
|
||
# 登录 root 跑安装),跳过 ``su - root`` —— 否则会无限递归(``su - root``
|
||
# 进入的仍是 root,又走到这个分支,再次 su,永远停不下来)。见 issue #20。
|
||
|
||
if [ "$(id -u)" -eq 0 ] && [ "$USERNAME" != "root" ]; then
|
||
echo "[memory-tencentdb] Running as root, switching to $USERNAME for installation..."
|
||
|
||
# 验证前置条件
|
||
if [ ! -d "$HERMES_AGENT_DIR" ]; then
|
||
echo "[ERROR] Hermes agent not found at $HERMES_AGENT_DIR"
|
||
echo "[ERROR] Please run install_hermes_ubuntu.sh first."
|
||
exit 1
|
||
fi
|
||
|
||
# 切换到目标用户执行
|
||
TEMP_SCRIPT=$(mktemp /tmp/memory-tencentdb-install-XXXXXX.sh)
|
||
cp "${BASH_SOURCE[0]}" "$TEMP_SCRIPT"
|
||
chmod 755 "$TEMP_SCRIPT"
|
||
su - $USERNAME -c "bash $TEMP_SCRIPT" </dev/null
|
||
|
||
# 修复权限
|
||
echo "[memory-tencentdb] Fixing permissions..."
|
||
chown -R $USERNAME:$USERNAME "$USER_HOME"
|
||
|
||
rm -f "$TEMP_SCRIPT"
|
||
echo "[memory-tencentdb] Installation completed successfully"
|
||
exit 0
|
||
elif [ "$(id -u)" -eq 0 ]; then
|
||
# 当前是 root 且目标用户也是 root:直接以 root 跑后续安装逻辑,
|
||
# 不再走 ``su -`` 切换(避免 #20 的递归)。
|
||
echo "[memory-tencentdb] Running as root; target user is also root — installing in place."
|
||
fi
|
||
|
||
# ==================== 用户阶段(核心安装逻辑) ====================
|
||
|
||
echo "[memory-tencentdb] Installing memory-tencentdb plugin (user: $(whoami))..."
|
||
|
||
# 验证前置条件
|
||
if [ ! -d "$HERMES_AGENT_DIR" ]; then
|
||
echo "[ERROR] Hermes agent not found at $HERMES_AGENT_DIR"
|
||
echo "[ERROR] Please run install_hermes_ubuntu.sh first."
|
||
exit 1
|
||
fi
|
||
|
||
# 加载 hermes 环境(PATH 中需要 node/npm)
|
||
if [ -f /etc/profile.d/hermes-env.sh ]; then
|
||
source /etc/profile.d/hermes-env.sh
|
||
fi
|
||
|
||
# 确保统一根目录存在
|
||
mkdir -p "$MEMORY_TENCENTDB_ROOT"
|
||
|
||
# ---------- Step 0: 自动迁移旧路径(向后兼容) ----------
|
||
#
|
||
# 历史版本把 tdai 解压到 ~/tdai-memory-openclaw-plugin、数据放在 ~/memory-tdai。
|
||
# 现在统一收纳到 ~/.memory-tencentdb/ 之下,这里做一次性自动迁移。
|
||
# 已经在新位置时跳过;新旧都存在时打印警告并保留新位置不动。
|
||
|
||
migrate_legacy_dir() {
|
||
local legacy="$1"
|
||
local target="$2"
|
||
local label="$3"
|
||
if [ ! -e "$legacy" ]; then
|
||
return 0
|
||
fi
|
||
if [ -L "$legacy" ]; then
|
||
# 旧位置是 symlink,直接清掉
|
||
echo "[memory-tencentdb] Removing legacy symlink: $legacy"
|
||
rm -f "$legacy"
|
||
return 0
|
||
fi
|
||
if [ -e "$target" ]; then
|
||
echo "[memory-tencentdb] WARN: legacy $label dir exists at $legacy but new location $target also exists." >&2
|
||
echo "[memory-tencentdb] WARN: keeping new location; please review and remove $legacy manually if obsolete." >&2
|
||
return 0
|
||
fi
|
||
echo "[memory-tencentdb] Migrating legacy $label dir: $legacy -> $target"
|
||
mkdir -p "$(dirname "$target")"
|
||
mv "$legacy" "$target"
|
||
}
|
||
|
||
migrate_legacy_dir "$LEGACY_INSTALL_DIR" "$TDAI_INSTALL_DIR" "install"
|
||
migrate_legacy_dir "$LEGACY_DATA_DIR" "$TDAI_DATA_DIR" "data"
|
||
|
||
# ---------- Step 1: 通过 npm 下载包并提取到 $TDAI_INSTALL_DIR ----------
|
||
|
||
echo "[memory-tencentdb] Step 1: Downloading $NPM_PACKAGE via npm..."
|
||
|
||
# 清理旧安装
|
||
rm -rf "$TDAI_INSTALL_DIR"
|
||
|
||
# 使用临时目录通过 npm install 下载包
|
||
TEMP_DOWNLOAD=$(mktemp -d /tmp/memory-tencentdb-download-XXXXXX)
|
||
cd "$TEMP_DOWNLOAD"
|
||
npm init -y --silent > /dev/null 2>&1
|
||
npm install "$NPM_PACKAGE" --omit=dev 2>&1 | tail -5
|
||
|
||
# 包安装后位于 node_modules/@tencentdb-agent-memory/memory-tencentdb
|
||
PACK_DIR="$TEMP_DOWNLOAD/node_modules/@tencentdb-agent-memory/memory-tencentdb"
|
||
|
||
if [ ! -d "$PACK_DIR" ]; then
|
||
echo "[ERROR] Downloaded package directory not found at $PACK_DIR"
|
||
rm -rf "$TEMP_DOWNLOAD"
|
||
exit 1
|
||
fi
|
||
|
||
# 将包内容移动到目标安装目录
|
||
mkdir -p "$(dirname "$TDAI_INSTALL_DIR")"
|
||
cp -r "$PACK_DIR" "$TDAI_INSTALL_DIR"
|
||
|
||
echo "[memory-tencentdb] Package downloaded and extracted to $TDAI_INSTALL_DIR"
|
||
|
||
# ---------- Step 2: 安装 Gateway Node.js 依赖 ----------
|
||
|
||
echo "[memory-tencentdb] Step 2: Installing Gateway dependencies..."
|
||
|
||
cd "$TDAI_INSTALL_DIR"
|
||
|
||
echo "[memory-tencentdb] Running npm install (this may take a while)..."
|
||
npm install --omit=dev 2>&1 | tail -5
|
||
|
||
# 安装 tsx(Gateway 启动需要),优先本地安装
|
||
if ! npx tsx --version &>/dev/null; then
|
||
npm install tsx 2>&1 | tail -2
|
||
fi
|
||
|
||
echo "[memory-tencentdb] Gateway dependencies installed"
|
||
|
||
# ---------- Step 2.5: 将插件链接到 hermes 插件目录 ----------
|
||
|
||
echo "[memory-tencentdb] Step 2.5: Linking plugin into hermes plugins directory..."
|
||
|
||
HERMES_PLUGIN_DIR="$HERMES_AGENT_DIR/plugins/memory/memory_tencentdb"
|
||
PLUGIN_SRC_DIR="$TDAI_INSTALL_DIR/hermes-plugin/memory/memory_tencentdb"
|
||
|
||
# 移除旧链接/目录
|
||
rm -rf "$HERMES_PLUGIN_DIR"
|
||
|
||
# 创建 symlink 使 hermes 能发现插件
|
||
ln -sf "$PLUGIN_SRC_DIR" "$HERMES_PLUGIN_DIR"
|
||
|
||
echo "[memory-tencentdb] Plugin linked: $HERMES_PLUGIN_DIR -> $PLUGIN_SRC_DIR"
|
||
|
||
# ---------- Step 3: 提示用户手动开启 memory_tencentdb(不自动修改 config) ----------
|
||
|
||
echo "[memory-tencentdb] Step 3: Checking hermes config..."
|
||
|
||
# 插件已链接到 hermes 插件目录,但默认不自动启用,仅提示
|
||
if [ -f "$HERMES_CONFIG" ]; then
|
||
if sed -n '/^memory:/,/^[a-zA-Z]/p' "$HERMES_CONFIG" | grep -q "provider: memory_tencentdb"; then
|
||
echo "[memory-tencentdb] memory.provider already set to memory_tencentdb"
|
||
else
|
||
echo "[memory-tencentdb] Plugin installed but NOT enabled by default."
|
||
echo "[memory-tencentdb] To enable tdai memory, add/edit in $HERMES_CONFIG:"
|
||
echo ""
|
||
echo " memory:"
|
||
echo " provider: memory_tencentdb"
|
||
echo ""
|
||
fi
|
||
else
|
||
echo "[memory-tencentdb] WARN: $HERMES_CONFIG not found, please run install_hermes_ubuntu.sh first"
|
||
fi
|
||
|
||
# ---------- Step 4: 配置 Gateway 环境变量 ----------
|
||
|
||
echo "[memory-tencentdb] Step 4: Setting up Gateway environment..."
|
||
|
||
# 构建 Gateway 启动命令
|
||
# 使用 sh -c 包裹,先 cd 到插件目录再启动 Gateway(ESM 解析需要)
|
||
#
|
||
# 解析 node 绝对路径写入 GATEWAY_CMD(fix: issue #19)
|
||
# 当 Hermes 或独立 Gateway 以 systemd service 运行时,systemd 不会
|
||
# source 任何 user shell rc 文件,nvm/asdf 注入的 PATH 不存在。
|
||
# 用 `command -v node` 在 install 时解析绝对路径,并改用 Node 原生
|
||
# `--import tsx/esm`(Node >= 20.6 stable)替代 `npx tsx`,
|
||
# 让最终命令完全不依赖运行时 PATH。
|
||
NODE_BIN="$(command -v node || true)"
|
||
if [ -z "$NODE_BIN" ]; then
|
||
echo "[ERROR] 'node' not found in PATH; cannot generate Gateway start command." >&2
|
||
echo "[ERROR] If you installed Node via nvm/asdf, source the loader script first:" >&2
|
||
echo "[ERROR] source ~/.bashrc # or 'nvm use <version>'" >&2
|
||
exit 1
|
||
fi
|
||
echo "[memory-tencentdb] Resolved node: $NODE_BIN"
|
||
|
||
GATEWAY_CMD="sh -c 'cd $TDAI_INSTALL_DIR && exec \"$NODE_BIN\" --import tsx/esm src/gateway/server.ts'"
|
||
|
||
# ── 4a: /etc/profile.d(SSH 交互式登录场景) ──
|
||
# 写入 /etc/profile.d 持久化环境变量,供 SSH 手动执行 `hermes` 时使用。
|
||
# 注意:LLM 相关变量(API key、model 等)需要用户后续手动配置
|
||
ENVFILE="/etc/profile.d/memory-tencentdb-env.sh"
|
||
cat << ENVEOF | sudo tee "$ENVFILE" > /dev/null
|
||
# memory-tencentdb Gateway 环境变量
|
||
export MEMORY_TENCENTDB_GATEWAY_CMD="$GATEWAY_CMD"
|
||
export MEMORY_TENCENTDB_GATEWAY_HOST="127.0.0.1"
|
||
export MEMORY_TENCENTDB_GATEWAY_PORT="8420"
|
||
# LLM 配置(按需修改)
|
||
# export MEMORY_TENCENTDB_LLM_API_KEY="sk-..."
|
||
# export MEMORY_TENCENTDB_LLM_BASE_URL="https://api.openai.com/v1"
|
||
# export MEMORY_TENCENTDB_LLM_MODEL="gpt-4o"
|
||
ENVEOF
|
||
|
||
echo "[memory-tencentdb] Environment variables written to $ENVFILE"
|
||
|
||
# ── 4b: ~/.hermes/.env(systemd service 场景) ──
|
||
# hermes-gateway 通过 systemd user service 启动时不会 source /etc/profile.d/*.sh,
|
||
# 但 hermes 的 run.py 启动时会 load_dotenv("~/.hermes/.env")。
|
||
# 因此必须将关键变量同步写入 .env,否则 systemd 场景下 Gateway 无法自动启动。
|
||
HERMES_ENV="$HERMES_HOME/.env"
|
||
|
||
_append_or_update_env() {
|
||
local key="$1"
|
||
local value="$2"
|
||
local file="$3"
|
||
if [ ! -f "$file" ]; then
|
||
touch "$file"
|
||
fi
|
||
# 移除已有的同名变量行(含注释掉的和带引号的),再追加
|
||
sed -i "/^${key}=/d" "$file"
|
||
sed -i "/^# *${key}=/d" "$file"
|
||
# python-dotenv 要求含空格/引号/特殊字符的值用双引号包裹
|
||
echo "${key}=\"${value}\"" >> "$file"
|
||
}
|
||
|
||
_append_or_update_env "MEMORY_TENCENTDB_GATEWAY_CMD" "$GATEWAY_CMD" "$HERMES_ENV"
|
||
_append_or_update_env "MEMORY_TENCENTDB_GATEWAY_HOST" "127.0.0.1" "$HERMES_ENV"
|
||
_append_or_update_env "MEMORY_TENCENTDB_GATEWAY_PORT" "8420" "$HERMES_ENV"
|
||
|
||
echo "[memory-tencentdb] Gateway env vars also written to $HERMES_ENV (for systemd service)"
|
||
|
||
# ---------- 清理 ----------
|
||
|
||
rm -rf "$TEMP_DOWNLOAD"
|
||
|
||
# ---------- 验证安装 ----------
|
||
|
||
echo ""
|
||
echo "=========================================="
|
||
echo "[memory-tencentdb] Installation Summary"
|
||
echo "=========================================="
|
||
echo " Root dir: $MEMORY_TENCENTDB_ROOT"
|
||
echo " tdai source: $TDAI_INSTALL_DIR"
|
||
echo " tdai data dir: $TDAI_DATA_DIR"
|
||
echo " Hermes config: $HERMES_CONFIG"
|
||
echo " Env file: $ENVFILE"
|
||
echo ""
|
||
echo " Installed files in tdai dir:"
|
||
ls -la "$TDAI_INSTALL_DIR"/ 2>/dev/null | head -20 || echo " (none)"
|
||
echo ""
|
||
|
||
# 验证 hermes 插件文件存在(在解压目录中)
|
||
PLUGIN_SRC="$TDAI_INSTALL_DIR/hermes-plugin/memory/memory_tencentdb"
|
||
MISSING=0
|
||
for f in __init__.py plugin.yaml client.py supervisor.py; do
|
||
if [ ! -f "$PLUGIN_SRC/$f" ]; then
|
||
echo " [WARN] Missing: $PLUGIN_SRC/$f"
|
||
MISSING=1
|
||
fi
|
||
done
|
||
|
||
if [ "$MISSING" -eq 0 ]; then
|
||
echo " [OK] All hermes plugin files present"
|
||
fi
|
||
|
||
# 验证 Gateway 入口存在
|
||
if [ -f "$TDAI_INSTALL_DIR/src/gateway/server.ts" ]; then
|
||
echo " [OK] Gateway entry point found"
|
||
else
|
||
echo " [WARN] Gateway server.ts not found at $TDAI_INSTALL_DIR/src/gateway/server.ts"
|
||
fi
|
||
|
||
# 验证 node_modules 已安装
|
||
if [ -d "$TDAI_INSTALL_DIR/node_modules" ]; then
|
||
echo " [OK] Gateway node_modules installed"
|
||
else
|
||
echo " [WARN] Gateway node_modules not found"
|
||
fi
|
||
|
||
echo ""
|
||
echo "[memory-tencentdb] Done!"
|
||
echo ""
|
||
echo " NOTE: Before using the memory plugin, configure LLM credentials in ~/.hermes/.env:"
|
||
echo " MEMORY_TENCENTDB_LLM_API_KEY=your-api-key"
|
||
echo " MEMORY_TENCENTDB_LLM_BASE_URL=https://api.openai.com/v1"
|
||
echo " MEMORY_TENCENTDB_LLM_MODEL=gpt-4o"
|
||
echo ""
|
||
echo " (For systemd-managed hermes-gateway, ~/.hermes/.env is the authoritative config."
|
||
echo " /etc/profile.d/ is only used for interactive SSH sessions.)"
|
||
echo ""
|