Files
HomeAgent/internal/plugin/proc_core.go
JianFeeeee 6d38a164eb chore(module): Go module 路径随托管地迁移 gitcode → github
## 背景

仓库早已迁移到 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 保留。
2026-10-08 10:40:17 +08:00

224 lines
7.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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{}