mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-10-15 12:15:16 +00:00
## 背景 仓库早已迁移到 GitHub(`github.com/JianFeeeee/HomeAgent`,CI/CD 都在那边跑), gitcode 保留为**国内只读镜像**(`ci.yml` 的 sync-gitcode-main 快进推送)。 但 go.mod 的 module 路径一直是 `gitcode.com/JianFeeeee/...` —— 既与权威源 不符,`meta.SourceURL`(AGPL §13 的源码提供入口)也指向镜像而非实际运行的代码。 ## 关键区分(这是本次改动安全的前提) 两个 module 是**不同**的,影响面差一个数量级: | module | 谁依赖 | 改它的后果 | |---|---|---| | `.../HomeAgent`(主仓) | **只有主仓自己** | 内部重构,零外部影响 | | `.../homeagent-sdk`(SDK 仓) | 主仓 49 文件 + 所有外部插件 | 需重编插件 | ★ 实测确认:**插件不 import 主仓 module**(`strings qq/plugin.bin | grep HomeAgent` = 0), 只依赖 SDK 仓。而插件与内核的边界是 **RPC 方法名字符串** (`plugin.init` / `tool.invoke`…,见 proc/protocol.go),**与 Go module 名无关**。 ⇒ module 改名**不改变任何 wire 协议**,现网已装的插件照常运行。 SDK 的 GitHub 仓名是 `homeagentsdk`(无连字符),module 路径必须与之精确匹配, 否则 `go get` 会因路径不符失败,故取 `github.com/JianFeeeee/homeagentsdk`。 ## 改动 - `go.mod` / `go.sum`:两个 module 路径 + require/replace - 277 个 Go 文件 + Makefile + 打包脚本的 import 与 ldflags 路径 - SDK 仓 `meta.CoreModule`、`tools/hmapdev` 的 module 声明与工程模板 - 对外文档/链接的仓库地址(README、PLUGIN_DEV、OVERVIEW 等) ## 明确**保留** gitcode 的地方(镜像机制,不能动) - `GITCODE_TOKEN`、`GC_URL="…@gitcode.com"`(CI 同步 job) - `gitcode.com/api/v5/...`(release 附件上传) - git remote `gitcode`(镜像拉取/推送) - `docs/zh/ci-cd-runbook.md`(整篇讲镜像运维) ## 验证 - `go build ./...` / `go vet ./...` 绿 - `go test ./... -count=1` → 48 包 ok / 0 FAIL - hmapdev 工具链编译通过(`third_party/homeagent-sdk/tools/hmapdev`) - 全仓残留检查:module 形态的 `gitcode.com/JianFeeeee` = 0 - 镜像机制抽验:GC_URL / @gitcode / api/v5 / remote 均完好 ## 顺带修正 `hmapdev/templates.go` 的注释此前写「gitcode 的模块不在 proxy.golang.org 上」—— module 已迁 GitHub,该论断不再成立。改为如实说明**为何仍默认 replace 到本机 SDK**: 离线可构建、避免版本漂移、内网可用。理由与托管地无关,故 replace 保留。
224 lines
7.7 KiB
Go
224 lines
7.7 KiB
Go
package plugin
|
||
|
||
import (
|
||
"github.com/JianFeeeee/HomeAgent/internal/plugin/proc"
|
||
isdk "github.com/JianFeeeee/HomeAgent/internal/sdk"
|
||
pubsdk "github.com/JianFeeeee/homeagentsdk/sdk"
|
||
)
|
||
|
||
// procCore 把内核的 *internal/sdk.PluginSDK 收窄成子进程插件可见的能力面。
|
||
//
|
||
// ❗ **必须用命名字段,不能嵌入** `*isdk.PluginSDK`:嵌入会让全部方法被提升,
|
||
// 外部插件通道就能经类型断言拿到 Supervisor()/Tracker()/Adapter()/Indexer()
|
||
// 这些内核内部机制——权限梯度退化成纸面约定。命名字段下只有下面显式写出的
|
||
// 方法存在,这才是 §3.8 说的「从 C ABI 表达能力的意外产物变成显式声明并强制的策略」。
|
||
//
|
||
// 另一个必要性:internal/sdk 的接口是公开 SDK 的**超集**(isdk.KnowledgeAPI
|
||
// 内嵌 pubsdk.KnowledgeAPI 再加 Stats()/Remove(),isdk.MemoryAPI 加 GraphData(),
|
||
// isdk.LLMAPI 加 Chat()/ReloadFromConfig()),Go 方法签名精确匹配下
|
||
// *isdk.PluginSDK 本就不满足 proc.CoreSDK。
|
||
//
|
||
// 内置插件走的仍是原路径(直接持 *isdk.PluginSDK,拿到全量接口),不受影响。
|
||
type procCore struct {
|
||
sdk *isdk.PluginSDK
|
||
}
|
||
|
||
// newProcCore 包装内核 SDK 供子进程插件使用。
|
||
func newProcCore(s *isdk.PluginSDK) procCore { return procCore{sdk: s} }
|
||
|
||
func (c procCore) PluginName() string { return c.sdk.PluginName() }
|
||
|
||
// ---- 能力访问器:内部超集接口 → 公开 SDK 接口 ----
|
||
//
|
||
// nil 保护是必要的:corehandler 用 `if xxx == nil` 判断能力不可用并返回
|
||
// errUnavailable,若把「类型化的 nil」透过去,判空会失效——插件收到的是
|
||
// panic 而不是"能力不可用"。
|
||
|
||
func (c procCore) Settings() pubsdk.SettingsAPI {
|
||
if s := c.sdk.Settings(); s != nil {
|
||
return s
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func (c procCore) Memory() pubsdk.MemoryAPI {
|
||
if m := c.sdk.Memory(); m != nil {
|
||
return m
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func (c procCore) TextMemory() pubsdk.TextMemoryAPI {
|
||
if m := c.sdk.TextMemory(); m != nil {
|
||
return m
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func (c procCore) DocMemory() pubsdk.DocMemoryAPI {
|
||
if m := c.sdk.DocMemory(); m != nil {
|
||
return m
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func (c procCore) Knowledge() pubsdk.KnowledgeAPI {
|
||
if k := c.sdk.Knowledge(); k != nil {
|
||
return k
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func (c procCore) LLM() pubsdk.LLMAPI {
|
||
if l := c.sdk.LLM(); l != nil {
|
||
return l
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func (c procCore) Social() pubsdk.SocialAPI {
|
||
if s := c.sdk.Social(); s != nil {
|
||
return s
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func (c procCore) PluginMgr() pubsdk.PluginMgrAPI {
|
||
if m := c.sdk.PluginMgr(); m != nil {
|
||
return m
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// ---- 注册面 ----
|
||
|
||
func (c procCore) RegisterTool(name string, def pubsdk.ToolDef, handler pubsdk.ToolHandler) error {
|
||
return c.sdk.RegisterTool(name, def, handler)
|
||
}
|
||
|
||
func (c procCore) RegisterStage(stage pubsdk.Stage, handler pubsdk.StageHandler, scope ...pubsdk.StageScope) {
|
||
c.sdk.RegisterStage(stage, handler, scope...)
|
||
}
|
||
|
||
func (c procCore) RegisterPluginAPI(name string) error {
|
||
return c.sdk.RegisterPluginAPI(name)
|
||
}
|
||
|
||
func (c procCore) RegisterOutputChannel(name string, caps int, desc string, def pubsdk.ChannelDef, handler pubsdk.ToolHandler) error {
|
||
return c.sdk.RegisterOutputChannel(name, caps, desc, def, handler)
|
||
}
|
||
|
||
func (c procCore) RegisterInputChannel(name string, def pubsdk.ChannelDef) error {
|
||
return c.sdk.RegisterInputChannel(name, def)
|
||
}
|
||
|
||
// ---- IO 注入 ----
|
||
|
||
func (c procCore) InjectText(source, channel, text string) {
|
||
c.sdk.InjectText(source, channel, text)
|
||
}
|
||
|
||
func (c procCore) InjectInterruptText(source, channel, text string) {
|
||
c.sdk.InjectInterruptText(source, channel, text)
|
||
}
|
||
|
||
func (c procCore) InjectTextNoMemory(source, channel, text string) {
|
||
c.sdk.InjectTextNoMemory(source, channel, text)
|
||
}
|
||
|
||
// ---- 带注入标志位(记忆/裁剪行为由插件在调用点声明)----
|
||
|
||
func (c procCore) InjectTextOpts(source, channel, text string, opts pubsdk.InjectOptions) {
|
||
c.sdk.InjectTextOpts(source, channel, text, opts)
|
||
}
|
||
|
||
func (c procCore) InjectInterruptTextOpts(source, channel, text string, opts pubsdk.InjectOptions) {
|
||
c.sdk.InjectInterruptTextOpts(source, channel, text, opts)
|
||
}
|
||
|
||
func (c procCore) InjectInputSyncOpts(source, channel, text string, opts pubsdk.InjectOptions) string {
|
||
// 这里用公共 SDK 的三参数 + opts 形态(返回回复文本),
|
||
// 不用内核内部那个 (eventType, payload) → *OutputEvent 的全量签名:
|
||
// 它会把内核 IO 事件结构暴露给外部插件。
|
||
return c.sdk.InjectInputSyncOpts(source, channel, text, opts)
|
||
}
|
||
|
||
// InjectInputSync 收窄为公开 SDK 的三参数文本形态。
|
||
//
|
||
// internal/sdk.PluginSDK 的同名方法是 (source, channel, eventType, payload)
|
||
// → *agentIO.OutputEvent,暴露了内核 IO 事件结构;外部插件只该看到回复文本。
|
||
// 取值方式与 C ABI 路径一致(internal/plugin/cabi/loader.go 的 case 47)。
|
||
func (c procCore) InjectInputSync(source, channel, text string) string {
|
||
out := c.sdk.InjectInputSync(source, channel, "text", map[string]interface{}{
|
||
"content": text,
|
||
})
|
||
if out == nil {
|
||
return ""
|
||
}
|
||
reply, _ := out.Payload["content"].(string)
|
||
return reply
|
||
}
|
||
|
||
// ---- 带媒体的 IO 注入 ----
|
||
//
|
||
// 三个方法都直接转调 internal/sdk 的同名方法:那一层已经是三参数 + blocks
|
||
// 的公开形态,不像 InjectInputSync 需要收窄。
|
||
|
||
func (c procCore) InjectInputMedia(source, channel, text string, blocks []pubsdk.ContentBlock) {
|
||
c.sdk.InjectInputMedia(source, channel, text, blocks)
|
||
}
|
||
|
||
func (c procCore) InjectInputMediaSync(source, channel, text string, blocks []pubsdk.ContentBlock) string {
|
||
return c.sdk.InjectInputMediaSync(source, channel, text, blocks)
|
||
}
|
||
|
||
func (c procCore) InjectInterruptMedia(source, channel, text string, blocks []pubsdk.ContentBlock) {
|
||
c.sdk.InjectInterruptMedia(source, channel, text, blocks)
|
||
}
|
||
|
||
func (c procCore) InjectInputMediaOpts(source, channel, text string, blocks []pubsdk.ContentBlock, opts pubsdk.InjectOptions) {
|
||
c.sdk.InjectInputMediaOpts(source, channel, text, blocks, opts)
|
||
}
|
||
|
||
func (c procCore) InjectInputMediaSyncOpts(source, channel, text string, blocks []pubsdk.ContentBlock, opts pubsdk.InjectOptions) string {
|
||
return c.sdk.InjectInputMediaSyncOpts(source, channel, text, blocks, opts)
|
||
}
|
||
|
||
func (c procCore) InjectInterruptMediaOpts(source, channel, text string, blocks []pubsdk.ContentBlock, opts pubsdk.InjectOptions) {
|
||
c.sdk.InjectInterruptMediaOpts(source, channel, text, blocks, opts)
|
||
}
|
||
|
||
// SetToolBlocks 转调 internal/sdk:插件工具注入的媒体块,内核在下一轮
|
||
// tool message 携带。
|
||
func (c procCore) SetToolBlocks(blocks []pubsdk.ContentBlock) {
|
||
c.sdk.SetToolBlocks(blocks)
|
||
}
|
||
|
||
// ---- 生命周期 ----
|
||
|
||
func (c procCore) SetAutoRestart(enabled bool) { c.sdk.SetAutoRestart(enabled) }
|
||
|
||
// 编译期确认收窄面正好满足子进程插件的能力契约。
|
||
var _ proc.CoreSDK = procCore{}
|
||
|
||
// procPluginAdapter 把 *proc.Plugin 适配到 registry 的 sdk.Plugin 接口。
|
||
//
|
||
// 两者只差 Start 的参数类型:registry 传 *isdk.PluginSDK(全量能力),
|
||
// 而子进程插件只该拿到收窄后的 proc.CoreSDK。转接在此发生,
|
||
// 权限收窄就成了**类型系统强制**的事,而不是约定(§3.8)。
|
||
//
|
||
// Name/Stop/Close 经嵌入指针提升;Close 对 registry.closeDynamic 可见,
|
||
// 故重载时能真正 kill 子进程——对比 cabi 路径的 Close 只做 dlclose,
|
||
// 而 dlclose 对 Go c-shared 是 no-op(§1.1,热重载静默失效的根因)。
|
||
type procPluginAdapter struct {
|
||
*proc.Plugin
|
||
}
|
||
|
||
// Start 把内核全量 SDK 收窄成子进程可见的能力面后启动进程。
|
||
func (a procPluginAdapter) Start(s *isdk.PluginSDK) error {
|
||
return a.Plugin.Start(newProcCore(s))
|
||
}
|
||
|
||
// 编译期确认适配器满足 registry 的插件接口。
|
||
var _ isdk.Plugin = procPluginAdapter{}
|