Files
HomeAgent/internal/plugin/dynamic.go
JianFeeeee b15d0bd114 refactor(plugin)!: 重编提示与模板路径改用 hmapdev;文档全面对齐 1.2.0
工具链在 SDK 1.2.0 更名为 hmapdev(原 plugindev)。核心侧三处功能耦合同步:

1. 用户可见报错:旧 C ABI 产物 / 协议版本不匹配 / 共享段版本不匹配
   三处「请用配套 plugindev 重编」→ hmapdev(对应两条测试断言同步)
2. e2e_template_test 的模板路径改为 tools/hmapdev/templates,
   并保留旧路径回退(旧 SDK 检出仍能跑测试)
3. 注释与文档同步

文档更新(用户可见面):
- assets/docs/{zh,en}/PLUGIN_DEV.md:工具链章节整体改为 hmapdev,
  补改名说明与 SDK 存储目录迁移;命令示例全部更新
- assets/docs/{zh,en}/ARCHITECTURE.md:**流程图与章节对齐 v1.2.0** ——
  · 向量化章节改为三层降级:统一多模态空间(主)→ 词嵌入 → TF-IDF(回退),
    写明「同指纹且同维度才参与融合」
  · 媒体记忆章节重写:媒体是一等记忆块(无独立 GC / 无引用计数 / 无描述式索引 /
    正文不再写 media marker),并写明 reembedStaleMedia 的跨空间迁移与写回
- README{,_EN}.md、docs/zh/plugin-interface-matrix.md:工具名与模板路径同步
  (历史条目标注「当时名为 plugindev」)

验证:go test ./internal/plugin/ ./internal/plugin/proc/ ok,
含 4 条真实模板 E2E(模板路径切换后仍通过)。
2026-09-12 12:42:43 +08:00

113 lines
3.1 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 (
"encoding/json"
"os"
"path/filepath"
)
const (
binEntry = "plugin.bin" // 子进程插件(纯 Go 二进制stdio JSON-RPC + 共享内存)
luaEntry = "main.lua"
skillEntry = "SKILL.md"
metaEntry = "plugin.json"
)
// legacyCABIEntries 是已退场的 C ABI 产物名。
//
// 保留这张表只为**给出明确错误**:插件目录里躺着 plugin.so 而内核不再认它时,
// 静默跳过会让「目录在但插件没加载」看起来像配置问题,而实际原因是需要用
// 新版 hmapdev 重编。
var legacyCABIEntries = []string{"plugin.so", "plugin.dll", "plugin.dylib"}
// entryKind 描述插件入口归属的加载通道。
//
// C ABI 通道(.so/.dll/.dylib已整体退场外部插件统一走子进程 + stdio RPC
// 三套独立 ABI 实现收敛为单一 RPC 实现§9.2)。
type entryKind int
const (
entryUnknown entryKind = iota
entryProc // plugin.bin —— 子进程 + stdio JSON-RPC + 共享内存
entryLua // main.lua
entrySkill // SKILL.md
)
func (k entryKind) String() string {
switch k {
case entryProc:
return "proc"
case entryLua:
return "lua"
case entrySkill:
return "skill"
}
return "unknown"
}
// classifyEntry 把 manifest 的 entry 字段映射到加载通道。
//
// entry 为空或声明已退场的 C ABI 产物时返回 entryUnknown
// 由调用方回退到目录探测(兼容无 manifest 的旧插件),
// 并在探测到 C ABI 残留时给出明确的重编提示。
func classifyEntry(entry string) entryKind {
switch entry {
case binEntry:
return entryProc
case luaEntry:
return entryLua
case skillEntry:
return entrySkill
}
return entryUnknown
}
// detectEntryKind 先读 manifest 的 entry读不到则按目录内存在的入口文件推断。
//
// 注意:存量插件的 plugin.json 可能仍写着 "plugin.so"(工具链已不再据此分派,
// 但历史产物里有),此时 classifyEntry 返回 unknown靠目录探测找到 plugin.bin。
func detectEntryKind(plgDir string) entryKind {
if mft := readManifest(plgDir); mft != nil {
if k := classifyEntry(mft.Entry); k != entryUnknown {
return k
}
}
for _, probe := range []struct {
file string
kind entryKind
}{
{binEntry, entryProc},
{luaEntry, entryLua},
{skillEntry, entrySkill},
} {
if st, err := os.Stat(filepath.Join(plgDir, probe.file)); err == nil && !st.IsDir() {
return probe.kind
}
}
return entryUnknown
}
// hasLegacyCABIEntry 判断插件目录里是否只剩已退场的 C ABI 产物。
//
// 用于给出「需要重编」而非「插件不存在」的错误。
func hasLegacyCABIEntry(plgDir string) bool {
for _, name := range legacyCABIEntries {
if st, err := os.Stat(filepath.Join(plgDir, name)); err == nil && !st.IsDir() {
return true
}
}
return false
}
func readManifest(dir string) *PluginManifest {
data, err := os.ReadFile(filepath.Join(dir, metaEntry))
if err != nil {
return nil
}
var m PluginManifest
if err := json.Unmarshal(data, &m); err != nil {
return nil
}
return &m
}