mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-09-21 17:38:10 +00:00
feat: add package/build.sh cross-platform build script
- build.sh supports linux/amd64, linux/arm64, darwin/amd64, darwin/arm64, windows/amd64, and 'all' for matrix build - Components: homed (CGO, Linux/macOS-only), waiter (all platforms), gui - Platform matrix documented in script header - waiter: extract setRawMode into rawmode_linux.go + rawmode_other.go for cross-platform support - Add cmd/gui/node_modules/ and dist/ to .gitignore - Fix .gitignore waiter pattern to not ignore cmd/waiter/ directory
This commit is contained in:
7
.gitignore
vendored
7
.gitignore
vendored
@ -1,5 +1,5 @@
|
||||
homed
|
||||
waiter
|
||||
/waiter
|
||||
*.exe
|
||||
*.log
|
||||
*.test
|
||||
@ -14,3 +14,8 @@ internal/meta/
|
||||
|
||||
*.hmap
|
||||
dev/
|
||||
|
||||
# GUI
|
||||
cmd/gui/node_modules/
|
||||
cmd/gui/dist/
|
||||
|
||||
|
||||
@ -11,7 +11,6 @@ import (
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -238,34 +237,5 @@ func printServerOutput(content string) {
|
||||
}
|
||||
}
|
||||
|
||||
func setRawMode(fd int) (func(), error) {
|
||||
type termios struct {
|
||||
Iflag uint32
|
||||
Oflag uint32
|
||||
Cflag uint32
|
||||
Lflag uint32
|
||||
Cc [20]byte
|
||||
Ispeed uint32
|
||||
Ospeed uint32
|
||||
}
|
||||
const (
|
||||
TCGETS = 0x5401
|
||||
TCSETS = 0x5402
|
||||
ICANON = 0x2
|
||||
ECHO = 0x8
|
||||
)
|
||||
var old termios
|
||||
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), TCGETS, uintptr(unsafe.Pointer(&old))); err != 0 {
|
||||
return func() {}, fmt.Errorf("ioctl TCGETS: %v", err)
|
||||
}
|
||||
new := old
|
||||
new.Lflag &^= ICANON | ECHO
|
||||
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), TCSETS, uintptr(unsafe.Pointer(&new))); err != 0 {
|
||||
return func() {}, fmt.Errorf("ioctl TCSETS: %v", err)
|
||||
}
|
||||
return func() {
|
||||
syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), TCSETS, uintptr(unsafe.Pointer(&old)))
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
|
||||
18
cmd/waiter/rawmode_defs.go
Normal file
18
cmd/waiter/rawmode_defs.go
Normal file
@ -0,0 +1,18 @@
|
||||
package main
|
||||
|
||||
type termios struct {
|
||||
Iflag uint32
|
||||
Oflag uint32
|
||||
Cflag uint32
|
||||
Lflag uint32
|
||||
Cc [20]byte
|
||||
Ispeed uint32
|
||||
Ospeed uint32
|
||||
}
|
||||
|
||||
const (
|
||||
TCGETS = 0x5401
|
||||
TCSETS = 0x5402
|
||||
ICANON = 0x2
|
||||
ECHO = 0x8
|
||||
)
|
||||
23
cmd/waiter/rawmode_linux.go
Normal file
23
cmd/waiter/rawmode_linux.go
Normal file
@ -0,0 +1,23 @@
|
||||
//go:build linux
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func setRawMode(fd int) (func(), error) {
|
||||
var old termios
|
||||
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), TCGETS, uintptr(unsafe.Pointer(&old))); err != 0 {
|
||||
return func() {}, err
|
||||
}
|
||||
new := old
|
||||
new.Lflag &^= ICANON | ECHO
|
||||
if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), TCSETS, uintptr(unsafe.Pointer(&new))); err != 0 {
|
||||
return func() {}, err
|
||||
}
|
||||
return func() {
|
||||
syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), TCSETS, uintptr(unsafe.Pointer(&old)))
|
||||
}, nil
|
||||
}
|
||||
9
cmd/waiter/rawmode_other.go
Normal file
9
cmd/waiter/rawmode_other.go
Normal file
@ -0,0 +1,9 @@
|
||||
//go:build !linux
|
||||
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func setRawMode(fd int) (func(), error) {
|
||||
return func() {}, fmt.Errorf("raw terminal mode not supported on this platform")
|
||||
}
|
||||
119
package/build.sh
Executable file
119
package/build.sh
Executable 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: blocked — gojieba CXX flags + dlfcn.h not available in 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}" ;;
|
||||
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}" ;;
|
||||
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
|
||||
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
|
||||
echo "[SKIP] homed ${plat} — gojieba CXX flags + dlfcn.h unavailable in MinGW cross-compiler"
|
||||
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
|
||||
135
审查.md
Normal file
135
审查.md
Normal file
@ -0,0 +1,135 @@
|
||||
# HomeAgent 代码审查报告(第三轮逐行全面复审)
|
||||
|
||||
审查日期: 2026-07-18
|
||||
|
||||
## 审查范围
|
||||
|
||||
- `cmd/homed/main.go` (453 行)
|
||||
- `internal/agent/core/agent.go` (3156 行), `stages.go` (155 行), `context.go` (252 行), `plugin_health.go` (128 行)
|
||||
- `internal/agent/core/stages_test.go` (199 行)
|
||||
- `internal/agent/api/provider.go` (783 行)
|
||||
- `internal/agent/io/channel.go` (636 行)
|
||||
- `internal/sdk/` 全部 9 个文件 (510 行)
|
||||
- `internal/plugin/` registry.go (426 行), plugin.go (295 行), dynamic.go (142 行)
|
||||
- `internal/plugin/cabi/loader.go` (651 行)
|
||||
- `internal/lua/vm.go` (403 行)
|
||||
- `internal/events/bus.go` (83 行)
|
||||
- `internal/config/registry.go` (700 行)
|
||||
- `internal/memory/graph.go` (766 行), indexer.go (312 行), static_embedder.go (369 行), clean_text.go (57 行)
|
||||
- `internal/memory/document/document.go` (471 行)
|
||||
- SDK 仓库 `sdk/` 全部 6 个 API 文件 + `plugin_test.go` (~700 行)
|
||||
- SDK 仓库 `tools/plugindev/templates.go`
|
||||
- 架构文档 4 份 (`docs/zh/`)
|
||||
|
||||
---
|
||||
|
||||
## Bug 级别
|
||||
|
||||
### A-1 `processTextInput` 缺失 `runStage(StageOnInput)` 调用
|
||||
|
||||
- **位置**: `internal/agent/core/agent.go:494-562`
|
||||
- **严重程度**: 高
|
||||
- **描述**: `processTextInput` 创建了 `stageCtx` (行515) 但从未调用 `a.runStage(sdk.StageOnInput, stageCtx)`。注册了 `on_input` 阶段的插件对文本输入完全不可见。
|
||||
- **对照**: `processMediaInput` (行410) 正确调用了 `runStage(StageOnInput)`。
|
||||
- **影响**: 所有文本输入的 `on_input` 阶段钩子静默失效。黑名单、限流、短路回复等功能对文本输入无效。
|
||||
- **文档对照**: ARCHITECTURE.md 完整链路图中 `processTextInput()` 下方第一支路即为 `on_input stage`。
|
||||
|
||||
### A-2 `processTextInput` 缺失 `publishEvent(EventRawInput)`
|
||||
|
||||
- **位置**: `internal/agent/core/agent.go:494-562`
|
||||
- **严重程度**: 中
|
||||
- **描述**: `processTextInput` 没有发布 `EventRawInput` 事件。
|
||||
- **对照**: `processMediaInput` (行405) 正确调用了 `a.publishEvent(events.EventRawInput, ...)`。
|
||||
- **影响**: 订阅 `raw_input` 事件的插件/WebUI 收不到文本输入的原始事件。
|
||||
|
||||
### A-4 `processMediaInput` 缺失 `emitMemoryCandidate` 调用
|
||||
|
||||
- **位置**: `internal/agent/core/agent.go:371-436`
|
||||
- **严重程度**: 中
|
||||
- **描述**: 媒体输入处理完成后,没有调用 `emitMemoryCandidate`(文本处理在行560对应调用)。图片/音频对话不会被捕获到文本记忆层。
|
||||
- **影响**: `text memory` 中缺失多模态交互记录。
|
||||
|
||||
### A-5 `StageOnInput` 在 `context.Append` 之后执行
|
||||
|
||||
- **位置**: `internal/agent/core/agent.go:389-410`
|
||||
- **严重程度**: 低
|
||||
- **描述**: `processMediaInput` 中行389 `a.context.Append()` 先执行,行410 `runStage(StageOnInput)` 后执行。插件在 `on_input` 阶段无法阻止上下文被追加。
|
||||
- **文档对照**: ARCHITECTURE.md 描述 `on_input` 为"消息到 Agent,零处理",应在任何处理之前执行。
|
||||
|
||||
### A-6 `lua/vm.go` 中 `http_get`/`http_post` 返回 mock 数据
|
||||
|
||||
- **位置**: `internal/lua/vm.go:69-80`
|
||||
- **严重程度**: 高
|
||||
- **描述**: `http_get` 返回 `{"url":...,"body":"mock","status":200}`,`http_post` 返回 `{"url":...,"body":...,"status":200}`。没有实际 HTTP 网络调用。
|
||||
- **文档对照**: ADAPTER.md 第93-95行明确说明 `http_get(url)` 和 `http_post(url, body)` 应发起真实 HTTP 请求。
|
||||
- **影响**: Lua 适配器脚本中尝试使用 `http_get`/`http_post` 获取外部数据的功能均静默失败。
|
||||
|
||||
---
|
||||
|
||||
## 功能限制
|
||||
|
||||
### A-7 C ABI 注册阶段时未传递 `StageScope`
|
||||
|
||||
- **位置**: `internal/plugin/cabi/loader.go:285-304`
|
||||
- **描述**: C ABI 的 `CORE_REGISTER_STAGE` (methodID=2) 调用 `s.RegisterStage(sdk.Stage(st), handler)`,没有 scope 参数,总是使用 `StageScopeGlobal`。外部 `.so` 插件无法使用 `StageScopeOwnTools`。
|
||||
- **对照**: 公共 SDK `sdk/plugin.go:237` 的 `RegisterStage` 支持 `...scope` 变参。
|
||||
|
||||
---
|
||||
|
||||
## 顺序/次要问题
|
||||
|
||||
### A-8 `processConsolidation` 未注入 source context
|
||||
|
||||
- **位置**: `internal/agent/core/agent.go:2567`
|
||||
- **描述**: `processConsolidation` 直接传 `&sdk.StageContext{RawMessage: input}`,没有调用 `injectSourceContext`。整理任务在 `process()` 内部的阶段钩子中无法获取来源/通道信息。
|
||||
|
||||
---
|
||||
|
||||
## 已确认无问题的功能
|
||||
|
||||
1. **SDK 接口映射**: `internal/sdk/` 的 5 组 type alias (`MemoryAPI`, `KnowledgeAPI`, `SettingsAPI`, `LLMAPI`, `DocMemoryAPI`) 全部正确对齐 `pubsdk`,impl 包装器逐一桥接。
|
||||
|
||||
2. **C ABI 45 个 dispatch 方法**: 全部覆盖 SDK API,JSON 序列化/反序列化正确,无遗漏。方法列表:
|
||||
- 1: CORE_REGISTER_TOOL
|
||||
- 2: CORE_REGISTER_STAGE
|
||||
- 3: CORE_REGISTER_OUTPUT_CH
|
||||
- 4: CORE_REGISTER_PLUGIN_API
|
||||
- 5-7: CORE_INJECT_TEXT / INJECT_INTERRUPT_TEXT / INJECT_TEXT_NO_MEMORY
|
||||
- 8: CORE_SET_AUTO_RESTART
|
||||
- 9-13: CORE_MEMORY_RECALL / COMMIT / INTROSPECT / MERGE / PURGE
|
||||
- 14: CORE_DOC_QUERY
|
||||
- 15: CORE_KNOWLEDGE_SEARCH
|
||||
- 16-18: CORE_SETTINGS_GET / SET / REGISTER_DEF
|
||||
- 19-20: CORE_LLM_LIST_SOURCES / SET_SOURCE
|
||||
- 21-22: CORE_SOCIAL_GET_PERSON / GET_NETWORK
|
||||
- 23-24: CORE_SUBSCRIBE / UNSUBSCRIBE
|
||||
- 25: CORE_FREE_STRING
|
||||
- 26-31: CORE_SETTINGS_GET_CORE / SET_CORE / LIST_CORE / GET_PLUGIN / SET_PLUGIN / LIST_PLUGIN
|
||||
- 32-34: CORE_DOC_INSERT / REMOVE / STATS
|
||||
- 35-36: CORE_KNOWLEDGE_ADD / LIST
|
||||
- 37: CORE_LLM_CURRENT_SOURCE
|
||||
- 38-40: CORE_SOCIAL_GET_TRAIT / GET_RELATIONS / LIST_PERSONS
|
||||
- 41: CORE_TEXT_MEMORY_APPEND
|
||||
- 42-45: CORE_SETTINGS_LIST / DEFS / DUMP / PLUGINS
|
||||
|
||||
3. **`pipeline.go` 非死代码**: `internal/memory/pipeline/pipeline.go` 在 `cmd/homed/main.go:122-130` 中被引用和启动。
|
||||
|
||||
4. **`emitMemoryCandidate` 被消费**: `cmd/homed/main.go:213-247` 的 goroutine 消费 `iom.OutputChan()` 中的 `memory_candidate` 事件,写入 `textMem` 和 `distiller`。
|
||||
|
||||
5. **向量表示跨会话兼容**: `StaticEmbedder` / `TFIDFVectorizer` 的 `Vectorize()` 是纯函数(基于词袋权重),不依赖会话状态。
|
||||
|
||||
6. **`processConsolidation` 不发射记忆候选**: 行2579注释说明这是有意为之,防止任务文本被蒸馏进图库造成污染。
|
||||
|
||||
---
|
||||
|
||||
## 汇总
|
||||
|
||||
| 编号 | 级别 | 文件 | 行 | 描述 |
|
||||
|------|------|------|----|------|
|
||||
| A-1 | **Bug** | `agent.go` | 494-562 | `processTextInput` 未调用 `StageOnInput` |
|
||||
| A-2 | **Bug** | `agent.go` | 494-562 | `processTextInput` 未发布 `EventRawInput` |
|
||||
| A-4 | **Bug** | `agent.go` | 371-436 | `processMediaInput` 未调用 `emitMemoryCandidate` |
|
||||
| A-5 | **Bug** | `agent.go` | 389-410 | `StageOnInput` 在 `context.Append` 之后执行 |
|
||||
| A-6 | **Bug** | `lua/vm.go` | 69-80 | `http_get`/`http_post` 返回 mock |
|
||||
| A-7 | 限制 | `cabi/loader.go` | 285-304 | C ABI 缺 `StageScopeOwnTools` 支持 |
|
||||
| A-8 | 顺序 | `agent.go` | 2567 | `processConsolidation` 未注入 source context |
|
||||
Reference in New Issue
Block a user