v0.7.2: 根目录清理 + Agent 心跳重构 + 内嵌 ONNX 模型

- 根目录清理: branding/docs/knowledge -> assets/, package/tools/deploy -> deploy/
- meta.go: Version 0.7.2, SDKCompatibleVersion 语义改为最高兼容
- Makefile: 版本回退 0.7.2
- registry.go: 系统提示词改用 meta.Version 格式化
- Agent 心跳: reorgGraph 拆分为三个独立循环(archive/merge/review),各自可配间隔
- GraphDB: 新增 sentences 表 + 关系句子溯源 + ClearSentenceID + CleanupOrphanedSentences
- Knowledge: 支持词嵌入向量化器
- NLP 四阶段流水线: Parse -> Extract -> Verify -> Fuse + SentenceRef
- 移除远程 HTTP 解析器(remote_parser.go)
- 新增内嵌 ONNX 模型(vocab + dep_parser.onnx):
  +build onnxruntime: 全量 ONNX Runtime 推理
  !build onnxruntime: 内嵌词表规则式降级解析器
- config: core.agent.onnx_model_path 替代 dep_parser_url
This commit is contained in:
JianFeeeee
2026-07-28 09:56:26 +08:00
parent 1cb3e87dde
commit 2c5f9ff262
47 changed files with 26141 additions and 242 deletions

119
deploy/packaging/build.sh Normal file
View File

@ -0,0 +1,119 @@
#!/usr/bin/env bash
set -euo pipefail
PROJECT_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
BUILD_DIR="${PROJECT_ROOT}/build"
VERSION="${VERSION:-$(git -C "$PROJECT_ROOT" describe --tags --dirty 2>/dev/null || echo "0.7.1")}"
COMMIT="${COMMIT:-$(git -C "$PROJECT_ROOT" rev-parse --short HEAD 2>/dev/null || echo "unknown")}"
BUILD_TIME="${BUILD_TIME:-$(date -u '+%Y-%m-%dT%H:%M:%SZ')}"
GO="${GO:-$(command -v go 2>/dev/null || echo "/home/jianf/go1.26.5/go/bin/go")}"
LDFLAGS="-X gitcode.com/JianFeeeee/HomeAgent/internal/meta.Version=${VERSION} -X gitcode.com/JianFeeeee/HomeAgent/internal/meta.Commit=${COMMIT} -X gitcode.com/JianFeeeee/HomeAgent/internal/meta.BuildTime=${BUILD_TIME}"
TARGET="${1:-native}"
COMPONENT="${2:-all}"
# ---- platform matrix ----
# homed: linux/amd64 + linux/arm64 (CGO), macOS native-only (no osxcross),
# windows/amd64 (MinGW)
# waiter: all platforms (CGO-free, raw terminal mode is a no-op on non-Linux)
# gui: electron-builder handles cross-platform natively
case "$TARGET" in
native) GOOS="" GOARCH="" ;;
linux/amd64) GOOS=linux GOARCH=amd64 CC="${CC:-}" ;;
linux/arm64) GOOS=linux GOARCH=arm64 CC="${CC:-aarch64-linux-gnu-gcc}" CXX="${CXX:-aarch64-linux-gnu-g++}" ;;
darwin/amd64) GOOS=darwin GOARCH=amd64 CC="${CC:-}" ;;
darwin/arm64) GOOS=darwin GOARCH=arm64 CC="${CC:-}" ;;
windows/amd64) GOOS=windows GOARCH=amd64 CC="${CC:-x86_64-w64-mingw32-gcc}" CXX="${CXX:-x86_64-w64-mingw32-g++}" ;;
all)
"$0" linux/amd64 "$COMPONENT"
"$0" linux/arm64 "$COMPONENT"
"$0" darwin/amd64 "$COMPONENT"
"$0" darwin/arm64 "$COMPONENT"
"$0" windows/amd64 "$COMPONENT"
exit 0
;;
*)
echo "Unknown target: $TARGET"
echo "Usage: $0 [native|linux/amd64|linux/arm64|darwin/amd64|darwin/arm64|windows/amd64|all]"
echo " [all|homed|waiter|gui]"
exit 1
esac
if [ -n "${GOOS:-}" ]; then
SUFFIX="${GOOS}_${GOARCH}"
export GOOS GOARCH
fi
if [ -n "${CC:-}" ]; then
export CC
fi
if [ -n "${CXX:-}" ]; then
export CXX
fi
export CGO_ENABLED="${CGO_ENABLED:-1}"
mkdir -p "$BUILD_DIR"
# ---- homed (CGO, sqlite3) ----
build_homed() {
local out="$BUILD_DIR/homed${SUFFIX:+_$SUFFIX}"
local plat="${GOOS:-linux}/${GOARCH:-amd64}"
if [ "$GOOS" = "darwin" ] && [ "${CC:-}" = "" ] && [ "$(uname)" != "Darwin" ]; then
echo "[SKIP] homed ${plat} — requires native macOS build (CGO + sqlite3, no osxcross)"
return
fi
if [ "$GOOS" = "windows" ]; then
out="${out}.exe"
fi
echo "[BUILD] homed ${plat}$out"
CGO_ENABLED=1 "$GO" build -trimpath -installsuffix dynlink \
-ldflags "$LDFLAGS" -o "$out" ./cmd/homed/
echo " OK ($(file "$out" | sed 's/.*: //') | $(du -h "$out" | cut -f1))"
}
# ---- waiter (cross-platform, CGO-free) ----
build_waiter() {
local plat="${GOOS:-linux}/${GOARCH:-amd64}"
local out="$BUILD_DIR/waiter${SUFFIX:+_$SUFFIX}"
if [ "$GOOS" = "windows" ]; then out="${out}.exe"; fi
echo "[BUILD] waiter ${plat}$out"
CGO_ENABLED=0 "$GO" build -trimpath -installsuffix dynlink \
-ldflags "$LDFLAGS" -o "$out" ./cmd/waiter/
echo " OK ($(du -h "$out" | cut -f1))"
}
# ---- gui (Electron) ----
build_gui() {
if [ -n "${GOOS:-}" ] && [ "$GOOS" != "$("$GO" env GOOS)" ]; then
echo "[SKIP] gui ${GOOS}/${GOARCH} — electron-builder handles cross-platform natively; run 'all' on CI host"
return
fi
local gui_dir="$PROJECT_ROOT/cmd/gui"
echo "[BUILD] gui → $BUILD_DIR/"
if [ ! -d "$gui_dir/node_modules" ]; then
echo " npm install..."
(cd "$gui_dir" && npm install --production)
fi
(cd "$gui_dir" && npx electron-builder --config "$gui_dir/package.json" \
--linux --win --mac \
--x64 --arm64 \
-p never \
-o "$BUILD_DIR")
echo " OK"
}
# ---- dispatch ----
case "$COMPONENT" in
all) build_homed; build_waiter; build_gui ;;
homed) build_homed ;;
waiter) build_waiter ;;
gui) build_gui ;;
*)
echo "Unknown component: $COMPONENT"
exit 1
esac

View File

@ -0,0 +1,326 @@
!include "MUI2.nsh"
!include "nsDialogs.nsh"
!include "LogicLib.nsh"
!include "WinVer.nsh"
!include "x64.nsh"
!ifndef VARIANT
!define VARIANT "full"
!endif
!define PRODUCT_NAME "HomeAgent"
!define PRODUCT_PUBLISHER "HomeAgent Team"
!define PRODUCT_VERSION "0.7.1"
!if "${VARIANT}" == "full"
!define PRODUCT_DISPLAY_NAME "HomeAgent 完整版"
!define OUTPUT_FILE "HomeAgent_v${PRODUCT_VERSION}_Full_win64.exe"
!define HAS_CORE 1
!define HAS_WAITER 1
!define HAS_GUI 1
!define HAS_CREDENTIALS 1
!else if "${VARIANT}" == "server"
!define PRODUCT_DISPLAY_NAME "HomeAgent 服务端"
!define OUTPUT_FILE "HomeAgent_v${PRODUCT_VERSION}_Server_win64.exe"
!define HAS_CORE 1
!define HAS_WAITER 0
!define HAS_GUI 0
!define HAS_CREDENTIALS 1
!else if "${VARIANT}" == "client"
!define PRODUCT_DISPLAY_NAME "HomeAgent 客户端"
!define OUTPUT_FILE "HomeAgent_v${PRODUCT_VERSION}_Client_win64.exe"
!define HAS_CORE 0
!define HAS_WAITER 1
!define HAS_GUI 1
!define HAS_CREDENTIALS 0
!else
!error "Unknown variant: ${VARIANT}"
!endif
Name "${PRODUCT_DISPLAY_NAME}"
OutFile "..\build\${OUTPUT_FILE}"
InstallDir "$PROGRAMFILES64\${PRODUCT_NAME}"
InstallDirRegKey HKLM "Software\${PRODUCT_NAME}" ""
RequestExecutionLevel admin
BrandingText "HomeAgent Installer"
SetCompressor /SOLID lzma
ShowInstDetails show
ShowUninstDetails show
Var apiKey
Var webuiUsername
Var webuiPassword
Var hwndApiKey
Var hwndUsername
Var hwndPassword
Var autoStart
Var startNow
Var hwndAutoStart
Var hwndStartNow
Function GenKey
nsExec::ExecToStack 'powershell -NoProfile -C "[System.Guid]::NewGuid().ToString($\'N$\')"'
Pop $0
Pop $1
${If} $1 == ""
StrCpy $1 "homeagent"
${Else}
StrCpy $1 $1 32
${EndIf}
Push $1
FunctionEnd
!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_DIRECTORY
!if "${HAS_CREDENTIALS}" == "1"
Page custom pageApiKeys pageApiKeysLeave
Page custom pageCredentials pageCredentialsLeave
!endif
!if "${HAS_CORE}" == "1"
Page custom pageStartupOptions pageStartupOptionsLeave
!endif
!insertmacro MUI_PAGE_INSTFILES
Page custom pageFinishSummary
!insertmacro MUI_LANGUAGE "SimpChinese"
!insertmacro MUI_LANGUAGE "English"
Function .onInit
!insertmacro MUI_LANGDLL_DISPLAY
StrCpy $autoStart "1"
StrCpy $startNow "1"
!if "${HAS_CREDENTIALS}" == "0"
Call GenKey
Pop $apiKey
!endif
FunctionEnd
!if "${HAS_CORE}" == "1"
Function pageStartupOptions
!insertmacro MUI_HEADER_TEXT "启动选项" "设置 HomeAgent 后端的启动方式"
nsDialogs::Create 1018
Pop $0
${If} $0 == error
Abort
${EndIf}
${NSD_CreateLabel} 0 5u 100% 20u "HomeAgent 后端 (homed) 是持续运行的服务进程。$\r$\n请选择启动方式:"
Pop $0
${NSD_CreateCheckBox} 10u 35u 100% 12u "开机自动启动后端 (添加到注册表启动项)"
Pop $hwndAutoStart
${If} $autoStart == "1"
${NSD_Check} $hwndAutoStart
${EndIf}
${NSD_CreateCheckBox} 10u 55u 100% 12u "安装完成后立即启动后端"
Pop $hwndStartNow
${If} $startNow == "1"
${NSD_Check} $hwndStartNow
${EndIf}
${NSD_CreateLabel} 10u 80u 100% 20u "如果选择开机自启动homed 将在每次登录 Windows 时自动运行。$\r$\n你也可以稍后从开始菜单手动启动。"
Pop $0
nsDialogs::Show
FunctionEnd
Function pageStartupOptionsLeave
${NSD_GetState} $hwndAutoStart $autoStart
${NSD_GetState} $hwndStartNow $startNow
FunctionEnd
!endif
!if "${HAS_CREDENTIALS}" == "1"
Function pageApiKeys
!insertmacro MUI_HEADER_TEXT "生成 API 密钥" "请复制此密钥,安装完成后将无法再次查看"
nsDialogs::Create 1018
Pop $0
${If} $0 == error
Abort
${EndIf}
Call GenKey
Pop $apiKey
${NSD_CreateLabel} 0 5u 100% 12u "API Key (用于 WebUI 和 GUI 认证):"
Pop $0
${NSD_CreateText} 0 20u 300u 12u $apiKey
Pop $hwndApiKey
${NSD_CreateLabel} 0 45u 100% 30u "请用鼠标选中文本框中的密钥并复制 (Ctrl+C)。$\r$\n此密钥同时用于:$\r$\n • Web 管理界面 (http://localhost:8080) 的 API 认证$\r$\n • 桌面 GUI 应用的自动连接配置"
Pop $0
nsDialogs::Show
FunctionEnd
Function pageApiKeysLeave
${NSD_GetText} $hwndApiKey $apiKey
FunctionEnd
Function pageCredentials
!insertmacro MUI_HEADER_TEXT "WebUI 登录设置" "设置 Web 管理界面的登录账号和密码"
nsDialogs::Create 1018
Pop $0
${If} $0 == error
Abort
${EndIf}
StrCpy $webuiUsername "admin"
${NSD_CreateLabel} 0 5u 70u 12u "用户名:"
Pop $0
${NSD_CreateText} 85u 5u 180u 12u $webuiUsername
Pop $hwndUsername
${NSD_CreateLabel} 0 25u 70u 12u "密码:"
Pop $0
${NSD_CreatePassword} 85u 25u 180u 12u ""
Pop $hwndPassword
${NSD_CreateLabel} 0 50u 100% 20u "这些凭证用于登录 Web 管理界面 http://localhost:8080"
Pop $0
nsDialogs::Show
FunctionEnd
Function pageCredentialsLeave
${NSD_GetText} $hwndUsername $webuiUsername
${NSD_GetText} $hwndPassword $webuiPassword
${If} $webuiPassword == ""
StrCpy $webuiPassword "homeagent"
${EndIf}
FunctionEnd
!endif
Function pageFinishSummary
!insertmacro MUI_HEADER_TEXT "安装完成" "以下为安装的关键信息,请截图或记录"
nsDialogs::Create 1018
Pop $0
${If} $0 == error
Abort
${EndIf}
${NSD_CreateLabel} 0 5u 100% 12u "API Key: $apiKey"
Pop $0
${NSD_CreateLabel} 0 20u 100% 12u "GUI 预配置: 已自动写入连接配置"
Pop $0
${NSD_CreateLabel} 0 35u 100% 12u "WebUI 地址: http://localhost:8080"
Pop $0
!if "${HAS_CREDENTIALS}" == "1"
${NSD_CreateLabel} 0 50u 100% 12u "WebUI 用户名: $webuiUsername"
Pop $0
${NSD_CreateLabel} 0 65u 100% 12u "WebUI 密码: $webuiPassword"
Pop $0
!endif
nsDialogs::Show
FunctionEnd
Section "Install" SEC_INSTALL
SetOutPath "$INSTDIR"
CreateDirectory "$INSTDIR\data"
CreateDirectory "$INSTDIR\data\log"
CreateDirectory "$INSTDIR\data\plugins"
CreateDirectory "$INSTDIR\data\adapters"
!if "${HAS_CORE}" == "1"
File "..\build\initconfig.exe"
File "..\build\homed.exe"
!endif
!if "${HAS_WAITER}" == "1"
File "..\build\waiter.exe"
!endif
!if "${HAS_GUI}" == "1"
SetOutPath "$INSTDIR\homeagent-gui-win32-x64"
File /r "..\build\homeagent-gui-win32-x64\*.*"
SetOutPath "$INSTDIR"
!endif
!if "${HAS_CORE}" == "1"
DetailPrint "初始化配置数据库..."
nsExec::Exec '"$INSTDIR\initconfig.exe" -data "$INSTDIR\data" -username "$webuiUsername" -password "$webuiPassword" -apikey "$apiKey"'
Pop $0
${If} $0 != 0
DetailPrint "警告: 数据库初始化可能未成功完成"
${EndIf}
!endif
!if "${HAS_GUI}" == "1"
DetailPrint "配置 GUI 连接..."
CreateDirectory "$APPDATA\homeagent-gui"
FileOpen $0 "$APPDATA\homeagent-gui\connections.json" w
FileWrite $0 '{$\r$\n "connections": [$\r$\n {$\r$\n "id": "local",$\r$\n "name": "本地",$\r$\n "url": "http://localhost:8080",$\r$\n "apiKey": "$apiKey"$\r$\n }$\r$\n ],$\r$\n "currentId": "local"$\r$\n}'
FileClose $0
; 同时写入 GUI 包目录作为备用(适配 UAC 提升后 $APPDATA 异常的情况)
CreateDirectory "$INSTDIR\homeagent-gui-win32-x64\resources\app"
FileOpen $0 "$INSTDIR\homeagent-gui-win32-x64\resources\app\connections.json" w
FileWrite $0 '{$\r$\n "connections": [$\r$\n {$\r$\n "id": "local",$\r$\n "name": "本地",$\r$\n "url": "http://localhost:8080",$\r$\n "apiKey": "$apiKey"$\r$\n }$\r$\n ],$\r$\n "currentId": "local"$\r$\n}'
FileClose $0
!endif
!if "${HAS_WAITER}" == "1"
DetailPrint "配置 CLI 连接..."
FileOpen $0 "$INSTDIR\waiter.yaml" w
FileWrite $0 "socket: $\"$INSTDIR\data\cli.sock$\"$\r$\napi_key: $apiKey$\r$\ndefault: local$\r$\nconnections:$\r$\n - name: local$\r$\n socket: $\"$INSTDIR\data\cli.sock$\"$\r$\n api_key: $apiKey$\r$\n"
FileClose $0
!endif
DetailPrint "创建快捷方式..."
CreateDirectory "$SMPROGRAMS\${PRODUCT_NAME}"
!if "${HAS_CORE}" == "1"
CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\HomeAgent Server.lnk" "$INSTDIR\homed.exe" '-data "$INSTDIR\data"' "$INSTDIR\homed.exe" 0
!endif
!if "${HAS_WAITER}" == "1"
CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\HomeAgent CLI.lnk" "$INSTDIR\waiter.exe" "" "$INSTDIR\waiter.exe" 0
!endif
!if "${HAS_GUI}" == "1"
CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\HomeAgent GUI.lnk" "$INSTDIR\homeagent-gui-win32-x64\homeagent-gui.exe" "" "$INSTDIR\homeagent-gui-win32-x64\homeagent-gui.exe" 0
!endif
DetailPrint "设置环境变量..."
WriteRegStr HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment" "HOMEAGENT_DATA" "$INSTDIR\data"
WriteRegStr HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment" "HOMEAGENT_SOCKET" "$INSTDIR\data\cli.sock"
!if "${HAS_CORE}" == "1"
${If} $autoStart == "1"
DetailPrint "设置开机自启动..."
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Run" "HomeAgent" '"$INSTDIR\homed.exe" -data "$INSTDIR\data"'
${EndIf}
!endif
DetailPrint "写入注册表..."
WriteRegStr HKLM "Software\${PRODUCT_NAME}" "" "$INSTDIR"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" "DisplayName" "${PRODUCT_DISPLAY_NAME}"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" "UninstallString" "$INSTDIR\Uninstall.exe"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" "InstallLocation" "$INSTDIR"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" "Publisher" "${PRODUCT_PUBLISHER}"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" "DisplayVersion" "${PRODUCT_VERSION}"
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" "NoModify" 1
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}" "NoRepair" 1
WriteUninstaller "$INSTDIR\Uninstall.exe"
!if "${HAS_CORE}" == "1"
${If} $startNow == "1"
DetailPrint "启动 HomeAgent 后端..."
Exec '"$INSTDIR\homed.exe" -data "$INSTDIR\data"'
${EndIf}
!endif
SectionEnd
Section "Uninstall"
!if "${HAS_CORE}" == "1"
DeleteRegValue HKCU "Software\Microsoft\Windows\CurrentVersion\Run" "HomeAgent"
!endif
Delete "$INSTDIR\Uninstall.exe"
Delete "$INSTDIR\initconfig.exe"
Delete "$INSTDIR\homed.exe"
Delete "$INSTDIR\waiter.exe"
Delete "$INSTDIR\waiter.yaml"
RMDir /r "$INSTDIR\data"
RMDir /r "$INSTDIR\homeagent-gui-win32-x64"
RMDir "$INSTDIR"
Delete "$SMPROGRAMS\${PRODUCT_NAME}\HomeAgent Server.lnk"
Delete "$SMPROGRAMS\${PRODUCT_NAME}\HomeAgent CLI.lnk"
Delete "$SMPROGRAMS\${PRODUCT_NAME}\HomeAgent GUI.lnk"
RMDir "$SMPROGRAMS\${PRODUCT_NAME}"
DeleteRegValue HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment" "HOMEAGENT_DATA"
DeleteRegValue HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment" "HOMEAGENT_SOCKET"
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}"
DeleteRegKey HKLM "Software\${PRODUCT_NAME}"
SectionEnd

View File

@ -0,0 +1,18 @@
Package: homeagent-client
Version: VERSION_PLACEHOLDER
Architecture: ARCH_PLACEHOLDER
Maintainer: HomeAgent Team <team@homeagent.ai>
Installed-Size: INSTALLED_SIZE_PLACEHOLDER
Depends: libc6 (>= 2.28)
Section: utils
Priority: optional
Homepage: https://github.com/trueagent/HomeAgent
Description: HomeAgent Client (CLI + GUI, no daemon)
HomeAgent is a personal AI home assistant that integrates
large language models with system automation.
.
This package includes the client components:
- waiter: command-line client
- homeagent-gui: desktop graphical interface
.
This variant connects to a remote HomeAgent server.

View File

@ -0,0 +1,17 @@
Package: homeagent-full
Version: VERSION_PLACEHOLDER
Architecture: ARCH_PLACEHOLDER
Maintainer: HomeAgent Team <team@homeagent.ai>
Installed-Size: INSTALLED_SIZE_PLACEHOLDER
Depends: libc6 (>= 2.28)
Section: utils
Priority: optional
Homepage: https://github.com/trueagent/HomeAgent
Description: HomeAgent Full Installation (Daemon + CLI + GUI)
HomeAgent is a personal AI home assistant that integrates
large language models with system automation.
.
This package includes the full suite:
- homed: the core daemon (background service)
- waiter: command-line client
- homeagent-gui: desktop graphical interface

View File

@ -0,0 +1,16 @@
Package: homeagent-server
Version: VERSION_PLACEHOLDER
Architecture: ARCH_PLACEHOLDER
Maintainer: HomeAgent Team <team@homeagent.ai>
Installed-Size: INSTALLED_SIZE_PLACEHOLDER
Depends: libc6 (>= 2.28)
Section: utils
Priority: optional
Homepage: https://github.com/trueagent/HomeAgent
Description: HomeAgent Server (Daemon + CLI, no GUI)
HomeAgent is a personal AI home assistant that integrates
large language models with system automation.
.
This package includes the server components:
- homed: the core daemon (background service)
- waiter: command-line client

View File

@ -0,0 +1,32 @@
#!/bin/sh
set -e
SERVICE_NAME="homeagent"
SERVICE_FILE="/lib/systemd/system/${SERVICE_NAME}.service"
HOMED_BIN="/usr/bin/homed"
DATA_DIR="/var/lib/homeagent"
case "$1" in
configure)
if [ -f "$HOMED_BIN" ]; then
mkdir -p "$DATA_DIR"
# 初始化凭据和数据库
if [ -x /usr/lib/homeagent/setup.sh ]; then
HOMEAGENT_DATA="$DATA_DIR" /usr/lib/homeagent/setup.sh || true
fi
# 注册 systemd 服务
if [ -f "$SERVICE_FILE" ]; then
systemctl daemon-reload 2>/dev/null || true
systemctl enable "$SERVICE_NAME" 2>/dev/null || true
fi
fi
;;
abort-upgrade|abort-remove|abort-deconfigure)
;;
*)
;;
esac
exit 0

View File

@ -0,0 +1,19 @@
#!/bin/sh
set -e
SERVICE_NAME="homeagent"
case "$1" in
remove|deconfigure)
if command -v systemctl >/dev/null 2>&1; then
systemctl stop "$SERVICE_NAME" 2>/dev/null || true
systemctl disable "$SERVICE_NAME" 2>/dev/null || true
fi
;;
upgrade)
;;
*)
;;
esac
exit 0

View File

@ -0,0 +1,111 @@
# HomeAgent RPM Spec
# Build: rpmbuild -ba homeagent.spec
#
# Define variant at build time:
# rpmbuild -ba --define "variant full" homeagent.spec
# rpmbuild -ba --define "variant server" homeagent.spec
# rpmbuild -ba --define "variant client" homeagent.spec
%define _prefix /usr
%define _bindir %{_prefix}/bin
%define _unitdir %{_prefix}/lib/systemd/system
%define _datadir %{_prefix}/share/homeagent
%define _varlibdir /var/lib/homeagent
%if %{undefined variant}
%define variant full
%endif
Name: homeagent-%{variant}
Version: %{_version}
Release: 1%{?dist}
Summary: HomeAgent - Personal AI Home Assistant
License: Proprietary
URL: https://github.com/trueagent/HomeAgent
Group: System Environment/Daemons
BuildArch: %{_arch}
%if "%{variant}" == "full" || "%{variant}" == "server"
Requires: systemd
%endif
%if "%{variant}" == "full"
Requires: libX11, libxcb, libdrm, mesa-libGL, nss, nspr, atk, at-spi2-atk, cairo, cups-libs, pango, gtk3
%endif
%if "%{variant}" == "client"
Requires: libX11, libxcb, libdrm, mesa-libGL, nss, nspr, atk, at-spi2-atk, cairo, cups-libs, pango, gtk3
%endif
%description
HomeAgent is a personal AI home assistant that integrates large language
models with system automation.
%if "%{variant}" == "full"
This package includes the full suite: homed (daemon), waiter (CLI),
and homeagent-gui (desktop GUI).
%else
%if "%{variant}" == "server"
This package includes the server components: homed (daemon) and waiter (CLI).
%else
%if "%{variant}" == "client"
This package includes the client components: waiter (CLI) and
homeagent-gui (desktop GUI). Connects to a remote HomeAgent server.
%endif
%endif
%endif
%install
mkdir -p %{buildroot}%{_bindir}
mkdir -p %{buildroot}%{_unitdir}
mkdir -p %{buildroot}%{_varlibdir}
%if "%{variant}" == "full" || "%{variant}" == "server"
install -m 755 %{_sourcedir}/homed %{buildroot}%{_bindir}/homed
install -m 644 %{_sourcedir}/homeagent.service %{buildroot}%{_unitdir}/homeagent.service
%endif
%if "%{variant}" == "full" || "%{variant}" == "client"
install -m 755 %{_sourcedir}/waiter %{buildroot}%{_bindir}/waiter
%endif
%if "%{variant}" == "full" || "%{variant}" == "client"
mkdir -p %{buildroot}%{_datadir}/homeagent-gui
cp -r %{_sourcedir}/homeagent-gui-linux-*/* %{buildroot}%{_datadir}/homeagent-gui/
%endif
%files
%defattr(-,root,root,-)
%if "%{variant}" == "full" || "%{variant}" == "server"
%{_bindir}/homed
%{_unitdir}/homeagent.service
%dir %{_varlibdir}
%endif
%if "%{variant}" == "full" || "%{variant}" == "client"
%{_bindir}/waiter
%endif
%if "%{variant}" == "full" || "%{variant}" == "client"
%dir %{_datadir}/homeagent-gui
%{_datadir}/homeagent-gui/*
%endif
%post
%if "%{variant}" == "full" || "%{variant}" == "server"
mkdir -p %{_varlibdir}
%systemd_post homeagent.service
%endif
%preun
%if "%{variant}" == "full" || "%{variant}" == "server"
%systemd_preun homeagent.service
%endif
%postun
%if "%{variant}" == "full" || "%{variant}" == "server"
%systemd_postun_with_restart homeagent.service
%endif
%changelog
* Mon Jul 20 2026 HomeAgent Team <team@homeagent.ai> - %{version}-1
- Initial Linux packaging

View File

@ -0,0 +1,63 @@
#!/usr/bin/env bash
# HomeAgent 首次初始化脚本
# 在安装后执行,生成凭据并初始化数据库
set -e
DATA_DIR="${HOMEAGENT_DATA:-/var/lib/homeagent}"
CRED_FILE="${DATA_DIR}/credentials.txt"
CONFIG_DB="${DATA_DIR}/config.db"
WAITER_CONF="${DATA_DIR}/waiter.yaml"
INITCONFIG_BIN="/usr/bin/initconfig"
# 如果已经初始化过,跳过
if [ -f "$CONFIG_DB" ] && [ -f "$CRED_FILE" ]; then
exit 0
fi
mkdir -p "$DATA_DIR"
# 生成随机凭据
API_KEY=$(cat /proc/sys/kernel/random/uuid 2>/dev/null | tr -d '-' || echo "homeagent$(date +%s)")
WEBUI_USER="${WEBUI_USER:-admin}"
WEBUI_PASS="${WEBUI_PASS:-$(openssl rand -hex 12 2>/dev/null || echo "homeagent")}"
# 初始化数据库
if [ -x "$INITCONFIG_BIN" ]; then
"$INITCONFIG_BIN" \
-data "$DATA_DIR" \
-username "$WEBUI_USER" \
-password "$WEBUI_PASS" \
-apikey "$API_KEY" 2>/dev/null
fi
# 保存凭据
cat > "$CRED_FILE" << CRED
===================================
HomeAgent 初始配置信息
请妥善保管,安装后仅此一份
===================================
WebUI 地址: http://localhost:8080
API Key: ${API_KEY}
WebUI 用户名: ${WEBUI_USER}
WebUI 密码: ${WEBUI_PASS}
CLI Socket: ${DATA_DIR}/cli.sock
===================================
CRED
chmod 600 "$CRED_FILE"
# 配置 waiter CLI
cat > "$WAITER_CONF" << WAITER
socket: "${DATA_DIR}/cli.sock"
api_key: ${API_KEY}
default: local
connections:
- name: local
socket: "${DATA_DIR}/cli.sock"
api_key: ${API_KEY}
WAITER
echo ""
echo "============================================"
echo " HomeAgent 初始化完成"
echo "============================================"
cat "$CRED_FILE"

View File

@ -0,0 +1,453 @@
#!/usr/bin/env bash
set -euo pipefail
PROJECT_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
BUILD_DIR="${PROJECT_ROOT}/build"
DIST_DIR="${PROJECT_ROOT}/dist/linux"
VERSION="${VERSION:-$(git -C "$PROJECT_ROOT" describe --tags --dirty 2>/dev/null || echo "0.7.1")}"
PACKAGE_ROOT="${PROJECT_ROOT}/deploy/packaging/linux"
GO="${GO:-$(command -v go 2>/dev/null || echo "/home/jianf/go1.26.5/go/bin/go")}"
ARCH="${1:-amd64}" # amd64 or arm64
ACTION="${2:-all}" # all, build, deb, tar, rpm
DEB_ARCH="$ARCH"
RPM_ARCH="$ARCH"
TAR_ARCH="$ARCH"
case "$ARCH" in
amd64) DEB_ARCH="amd64"; RPM_ARCH="x86_64"; TAR_ARCH="amd64" ;;
arm64) DEB_ARCH="arm64"; RPM_ARCH="aarch64"; TAR_ARCH="arm64" ;;
*) echo "Unknown arch: $ARCH (use amd64 or arm64)"; exit 1 ;;
esac
echo "=== HomeAgent Linux Packager ==="
echo "Version: $VERSION"
echo "Arch: $ARCH"
echo ""
# ---- prepare go.mod for Linux build: replace Windows SDK path with local clone ----
prepare_gomod() {
local gomod="$PROJECT_ROOT/go.mod"
local sdk_clone="/tmp/homeagent-sdk"
local patched=0
if grep -q 'replace gitcode.com/JianFeeeee/homeagent-sdk' "$gomod"; then
echo ">>> Updating go.mod: replacing Windows SDK path with local clone..."
if [ ! -d "$sdk_clone" ]; then
echo ">>> Cloning SDK to $sdk_clone..."
git clone git@gitcode.com:JianFeeeee/homeagent-sdk.git "$sdk_clone" 2>/dev/null || \
git clone https://gitcode.com/JianFeeeee/homeagent-sdk.git "$sdk_clone" 2>/dev/null || true
fi
if [ -d "$sdk_clone" ]; then
sed -i.bak "s|^replace gitcode.com/JianFeeeee/homeagent-sdk => .*|replace gitcode.com/JianFeeeee/homeagent-sdk => ${sdk_clone}|" "$gomod"
patched=1
else
echo "WARNING: Cannot clone SDK. Build may fail."
fi
fi
return $patched
}
restore_gomod() {
if [ -f "$PROJECT_ROOT/go.mod.bak" ]; then
echo ">>> Restoring original go.mod..."
mv "$PROJECT_ROOT/go.mod.bak" "$PROJECT_ROOT/go.mod"
fi
}
# ---- temporarily move .syso files (Windows COFF objects) for Linux builds ----
hide_syso() {
echo ">>> Hiding .syso files (Windows-only object files)..."
for dir in homed waiter; do
if [ -f "$PROJECT_ROOT/cmd/$dir/$dir.syso" ]; then
mv "$PROJECT_ROOT/cmd/$dir/$dir.syso" "$PROJECT_ROOT/cmd/$dir/$dir.syso.bak"
fi
done
}
restore_syso() {
for dir in homed waiter; do
if [ -f "$PROJECT_ROOT/cmd/$dir/$dir.syso.bak" ]; then
mv "$PROJECT_ROOT/cmd/$dir/$dir.syso.bak" "$PROJECT_ROOT/cmd/$dir/$dir.syso"
fi
done
}
# ensure both are always restored on exit
restore_all() { restore_gomod; restore_syso; }
trap restore_all EXIT
# ---- build Go binaries via existing build.sh ----
build_go() {
echo ">>> Building Go binaries for linux/$ARCH..."
prepare_gomod || true
hide_syso
bash "$PROJECT_ROOT/deploy/packaging/build.sh" "linux/$ARCH" "homed" 2>&1 || {
echo "WARNING: homed build failed (CGO/sqlite3 issue). Server/full packages may be incomplete."
}
bash "$PROJECT_ROOT/deploy/packaging/build.sh" "linux/$ARCH" "waiter" 2>&1 || {
echo "WARNING: waiter build failed."
}
local suffix="linux_${ARCH}"
local homed_bin="$BUILD_DIR/homed_$suffix"
local waiter_bin="$BUILD_DIR/waiter_$suffix"
if [ ! -f "$homed_bin" ]; then
echo "ERROR: homed binary not found at $homed_bin"
exit 1
fi
if [ ! -f "$waiter_bin" ]; then
echo "ERROR: waiter binary not found at $waiter_bin"
exit 1
fi
echo " homed: $homed_bin ($(du -h "$homed_bin" | cut -f1))"
echo " waiter: $waiter_bin ($(du -h "$waiter_bin" | cut -f1))"
echo ""
}
# ---- build GUI (manual directory assembly, avoids electron-packager network issues) ----
build_gui() {
local gui_dir="$PROJECT_ROOT/cmd/gui"
local gui_out="$BUILD_DIR/homeagent-gui-linux-${TAR_ARCH}"
if [ -d "$gui_out" ]; then
echo ">>> GUI already built at $gui_out (delete to rebuild)"
return
fi
echo ">>> Building GUI directory for linux/$ARCH..."
if [ ! -d "$gui_dir/node_modules" ]; then
echo " npm install..."
(cd "$gui_dir" && npm install --production)
fi
local electron_dir="$gui_dir/node_modules/electron/dist"
if [ ! -f "$electron_dir/electron" ]; then
echo " WARNING: electron binary not found at $electron_dir. GUI will be skipped."
return
fi
mkdir -p "$gui_out/resources/app/node_modules"
mkdir -p "$gui_out/resources/app/renderer"
# copy electron runtime (binary + shared libs)
cp -r "$electron_dir"/* "$gui_out/" 2>/dev/null
rm -f "$gui_out/resources/default_app.asar" 2>/dev/null
# copy app source
cp "$gui_dir/main.js" "$gui_out/resources/app/"
cp "$gui_dir/preload.js" "$gui_out/resources/app/"
cp "$gui_dir/package.json" "$gui_out/resources/app/"
cp "$gui_dir/renderer/index.html" "$gui_out/resources/app/renderer/"
cp "$gui_dir/renderer/app.js" "$gui_out/resources/app/renderer/"
cp "$gui_dir/renderer/style.css" "$gui_out/resources/app/renderer/" 2>/dev/null || true
cp "$gui_dir/renderer/mascot.svg" "$gui_out/resources/app/renderer/" 2>/dev/null || true
# production node_modules for app
if [ -d "$gui_dir/node_modules" ]; then
for mod in icojs; do
if [ -d "$gui_dir/node_modules/$mod" ]; then
cp -r "$gui_dir/node_modules/$mod" "$gui_out/resources/app/node_modules/"
fi
done
fi
# create desktop entry and symlink
cat > "$gui_out/resources/app/homeagent-gui.desktop" << DESKTOP
[Desktop Entry]
Name=HomeAgent
Comment=HomeAgent Desktop GUI
Exec=$gui_out/homeagent-gui
Terminal=false
Type=Application
Categories=Utility;
Icon=$gui_out/resources/app/icon.svg
DESKTOP
# create launcher script
cat > "$gui_out/homeagent-gui" << 'LAUNCHER'
#!/bin/sh
DIR="$(cd "$(dirname "$0")" && pwd)"
exec "$DIR/electron" "$DIR/resources/app" "$@"
LAUNCHER
chmod +x "$gui_out/homeagent-gui"
chmod +x "$gui_out/electron"
echo " GUI built: $gui_out ($(du -sh "$gui_out" | cut -f1))"
echo ""
}
# ---- stage files for a variant ----
stage_variant() {
local variant="$1"
local staging="$2"
local suffix="linux_${ARCH}"
echo ">>> Staging $variant..."
mkdir -p "$staging/usr/bin"
mkdir -p "$staging/etc/systemd/system"
mkdir -p "$staging/var/lib/homeagent"
local initconfig_bin="$BUILD_DIR/initconfig_$suffix"
case "$variant" in
full)
cp "$BUILD_DIR/homed_$suffix" "$staging/usr/bin/homed"
cp "$BUILD_DIR/waiter_$suffix" "$staging/usr/bin/waiter"
cp "$PROJECT_ROOT/deploy/homeagent.service" "$staging/etc/systemd/system/homeagent.service"
[ -f "$initconfig_bin" ] && cp "$initconfig_bin" "$staging/usr/bin/initconfig"
stage_setup "$staging"
stage_gui "$staging"
;;
server)
cp "$BUILD_DIR/homed_$suffix" "$staging/usr/bin/homed"
cp "$BUILD_DIR/waiter_$suffix" "$staging/usr/bin/waiter"
cp "$PROJECT_ROOT/deploy/homeagent.service" "$staging/etc/systemd/system/homeagent.service"
[ -f "$initconfig_bin" ] && cp "$initconfig_bin" "$staging/usr/bin/initconfig"
stage_setup "$staging"
;;
client)
cp "$BUILD_DIR/waiter_$suffix" "$staging/usr/bin/waiter"
stage_gui "$staging"
;;
esac
chmod 755 "$staging/usr/bin/"* 2>/dev/null || true
echo " OK"
}
stage_gui() {
local staging="$1"
local gui_src="$BUILD_DIR/homeagent-gui-linux-${TAR_ARCH}"
if [ -d "$gui_src" ]; then
mkdir -p "$staging/usr/lib/homeagent-gui"
cp -r "$gui_src"/* "$staging/usr/lib/homeagent-gui/"
cat > "$staging/usr/bin/homeagent-gui" << 'SCRIPT'
#!/bin/sh
exec /usr/lib/homeagent-gui/homeagent-gui "$@"
SCRIPT
chmod 755 "$staging/usr/bin/homeagent-gui"
else
echo " WARNING: GUI not built, skipping GUI staging"
fi
}
stage_setup() {
local staging="$1"
local setup_src="$PROJECT_ROOT/deploy/packaging/linux/setup.sh"
if [ -f "$setup_src" ]; then
mkdir -p "$staging/usr/lib/homeagent"
cp "$setup_src" "$staging/usr/lib/homeagent/setup.sh"
chmod 755 "$staging/usr/lib/homeagent/setup.sh"
fi
}
# ---- create .deb ----
build_deb() {
local variant="$1"
local staging="$2"
local deb_dir="${DIST_DIR}/deb"
mkdir -p "$deb_dir"
local pkg_name="homeagent-${variant}_${VERSION}_${DEB_ARCH}.deb"
local deb_root
deb_root="$(mktemp -d)"
mkdir -p "$deb_root/DEBIAN"
local control_file="$PACKAGE_ROOT/deb/control-${variant}"
local installed_size_kb
installed_size_kb=$(du -sk "$staging" | cut -f1)
sed -e "s/VERSION_PLACEHOLDER/$VERSION/g" \
-e "s/ARCH_PLACEHOLDER/$DEB_ARCH/g" \
-e "s/INSTALLED_SIZE_PLACEHOLDER/$installed_size_kb/g" \
"$control_file" > "$deb_root/DEBIAN/control"
if [ -f "$PACKAGE_ROOT/deb/postinst" ]; then
cp "$PACKAGE_ROOT/deb/postinst" "$deb_root/DEBIAN/postinst"
chmod 755 "$deb_root/DEBIAN/postinst"
fi
if [ -f "$PACKAGE_ROOT/deb/prerm" ]; then
cp "$PACKAGE_ROOT/deb/prerm" "$deb_root/DEBIAN/prerm"
chmod 755 "$deb_root/DEBIAN/prerm"
fi
cp -r "$staging"/* "$deb_root/" 2>/dev/null || true
echo ">>> Building .deb: $pkg_name"
fakeroot dpkg-deb --build "$deb_root" "$deb_dir/$pkg_name" 2>/dev/null || \
dpkg-deb --build "$deb_root" "$deb_dir/$pkg_name" 2>&1
rm -rf "$deb_root"
echo " Created: $deb_dir/$pkg_name ($(du -h "$deb_dir/$pkg_name" | cut -f1))"
}
# ---- create combined .tar.gz (all binaries, no variant split) ----
build_tar() {
local tar_dir="${DIST_DIR}/tar"
mkdir -p "$tar_dir"
local archive_name="homeagent_${VERSION}_linux_${TAR_ARCH}.tar.gz"
local archive_dir="homeagent-${VERSION}-linux-${TAR_ARCH}"
# build combined staging
local staging
staging="$(mktemp -d)"
mkdir -p "$staging/usr/bin" "$staging/usr/lib/homeagent"
# copy all available binaries
for bin in homed waiter initconfig; do
local src="$BUILD_DIR/${bin}_linux_${TAR_ARCH}"
[ -f "$src" ] && cp "$src" "$staging/usr/bin/$bin"
done
# setup script
local setup_src="$PROJECT_ROOT/deploy/packaging/linux/setup.sh"
[ -f "$setup_src" ] && cp "$setup_src" "$staging/usr/lib/homeagent/setup.sh"
# GUI if available
local gui_src="$BUILD_DIR/homeagent-gui-linux-${TAR_ARCH}"
if [ -d "$gui_src" ]; then
mkdir -p "$staging/usr/lib/homeagent-gui"
cp -r "$gui_src"/* "$staging/usr/lib/homeagent-gui/"
cat > "$staging/usr/bin/homeagent-gui" << 'SCRIPT'
#!/bin/sh
exec /usr/lib/homeagent-gui/homeagent-gui "$@"
SCRIPT
chmod 755 "$staging/usr/bin/homeagent-gui"
fi
chmod 755 "$staging/usr/bin/"* 2>/dev/null || true
echo ">>> Building .tar.gz: $archive_name"
(cd "$staging" && tar czf "$tar_dir/$archive_name" --transform "s|^\.|${archive_dir}|" .)
echo " Created: $tar_dir/$archive_name ($(du -h "$tar_dir/$archive_name" | cut -f1))"
rm -rf "$staging"
}
# ---- create .rpm (via fpm if available) ----
build_rpm() {
local variant="$1"
local staging="$2"
local rpm_dir="${DIST_DIR}/rpm"
mkdir -p "$rpm_dir"
local pkg_name="homeagent-${variant}-${VERSION}-1.${RPM_ARCH}.rpm"
# find fpm
local fpm_bin="$(command -v fpm 2>/dev/null || true)"
if [ -z "$fpm_bin" ]; then
fpm_bin="$(find /home -name "fpm" -type f -path "*/bin/*" 2>/dev/null | head -1 || true)"
fi
if [ -z "$fpm_bin" ]; then
echo " SKIP .rpm: fpm not installed. Install it with: gem install fpm"
echo " Alternatively, build RPM on Fedora/RHEL using:"
echo " rpmbuild -ba deploy/packaging/linux/homeagent.spec"
return
fi
# find or extract rpmbuild (fpm needs it)
local rpmbuild_dir="/tmp/rpmext"
if [ ! -f "$rpmbuild_dir/usr/bin/rpmbuild" ]; then
# try to extract from cached deb packages
local rpm_deb
rpm_deb="$(find /tmp -name "rpm_*.deb" -type f 2>/dev/null | head -1)"
if [ -z "$rpm_deb" ]; then
rpm_deb="$(find "$PROJECT_ROOT" -name "rpm_*.deb" -type f 2>/dev/null | head -1)"
fi
if [ -n "$rpm_deb" ]; then
mkdir -p "$rpmbuild_dir"
(cd "$rpmbuild_dir" && ar x "$rpm_deb" 2>/dev/null && \
tar --no-same-permissions -xf data.tar.zst --zstd 2>/dev/null) || true
for libdeb in /tmp/librpm*.deb; do
[ -f "$libdeb" ] && (cd "$rpmbuild_dir" && ar x "$libdeb" 2>/dev/null && \
tar --no-same-permissions -xf data.tar.zst --zstd 2>/dev/null) || true
done
fi
fi
if [ -f "$rpmbuild_dir/usr/bin/rpmbuild" ]; then
export PATH="$rpmbuild_dir/usr/bin:$PATH"
export LD_LIBRARY_PATH="$rpmbuild_dir/usr/lib/x86_64-linux-gnu${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
export RPM_CONFIGDIR="$rpmbuild_dir/usr/lib/rpm"
fi
echo ">>> Building .rpm via fpm: $pkg_name"
"$fpm_bin" -s dir -t rpm \
-n "homeagent-${variant}" \
-v "$VERSION" \
--iteration 1 \
-a "$RPM_ARCH" \
--description "HomeAgent ${variant^} package" \
--url "https://github.com/trueagent/HomeAgent" \
--license "Proprietary" \
-C "$staging" \
-p "$rpm_dir/$pkg_name" \
. 2>&1
echo " Created: $rpm_dir/$pkg_name"
}
# ---- main ----
main() {
local target_arch="$ARCH"
mkdir -p "$BUILD_DIR"
case "$ACTION" in
all|build)
build_go
build_gui
;;
esac
if [ "$ACTION" = "build" ]; then
echo ""
echo "=== Build complete. Binaries in $BUILD_DIR ==="
exit 0
fi
mkdir -p "$DIST_DIR"
for variant in full server client; do
echo ""
echo "=============================================="
echo " Packaging: $variant"
echo "=============================================="
local staging
staging=$(mktemp -d)
stage_variant "$variant" "$staging"
case "$ACTION" in
all|deb) build_deb "$variant" "$staging" ;;
esac
case "$ACTION" in
all|rpm) build_rpm "$variant" "$staging" ;;
esac
rm -rf "$staging"
done
case "$ACTION" in
all|tar) build_tar ;;
esac
echo ""
echo "=== Done! Packages in: $DIST_DIR ==="
echo ""
echo "Summary:"
find "$DIST_DIR" -type f \( -name "*.deb" -o -name "homeagent_*.tar.gz" -o -name "*.rpm" \) 2>/dev/null | sort | while read -r f; do
echo " $(du -h "$f" | cut -f1) $f"
done
}
main

47
deploy/scripts/deploy.sh Normal file
View File

@ -0,0 +1,47 @@
#!/usr/bin/env bash
set -euo pipefail
# HomeAgent 部署脚本
# 用法: cd <project-root> && sudo bash deploy/scripts/deploy.sh
PROJECT_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
BIN_DIR="/usr/local/bin"
DATA_DIR="/home/newqqagent"
SERVICE_FILE="/etc/systemd/system/homeagent.service"
echo "=== 构建 homed / waiter ==="
cd "$PROJECT_ROOT"
HOME=/root GOPATH=/root/go GOMODCACHE=/root/go/pkg/mod GOCACHE=/root/.cache/go-build make build build-cli
echo "=== 安装二进制 ==="
cp build/homed "$BIN_DIR/homed"
cp build/waiter "$BIN_DIR/waiter"
chmod 755 "$BIN_DIR/homed" "$BIN_DIR/waiter"
echo "=== 创建数据目录 ==="
mkdir -p "$DATA_DIR/plugins"
echo "=== 部署 QQ 插件 ==="
if [ -d "$PROJECT_ROOT/plugins/qq" ]; then
mkdir -p "$DATA_DIR/plugins/qq"
cp "$PROJECT_ROOT/plugins/qq/plugin.json" "$DATA_DIR/plugins/qq/"
cp "$PROJECT_ROOT/plugins/qq/plugin.so" "$DATA_DIR/plugins/qq/"
echo "QQ 插件已部署"
fi
echo "=== 安装 systemd 服务 ==="
cp "$(dirname "$0")/homeagent.service" "$SERVICE_FILE"
systemctl daemon-reload
echo ""
echo "=== 部署完成 ==="
echo ""
echo "启动: systemctl start homeagent"
echo "状态: systemctl status homeagent"
echo "日志: journalctl -u homeagent -f"
echo "停止: systemctl stop homeagent"
echo "WebUI: http://localhost:8080"
echo "CLI: /home/newqqagent/cli.sock"
echo ""
echo "首次使用请通过 WebUI → 设置 配置 API 密钥"
echo "工作目录: 在 WebUI 设置 → core.agent.workdir 中配置(如 /home/newqqagent/workspace"