Files
HomeAgent/internal/plugins/openclaw/plugin.go
root 2d314b3e9c 重构: 插件自注册 + .so 动态加载 + 中断打断机制
- 所有内置插件 init() 自注册 (plugin.RegisterFactory), 移除 main.go 硬编码
- 新增 .so 动态加载器 (internal/plugin/dynamic.go), 插件可编译为 plugin.so
- 新增 plugin.json 元数据 (internal/plugin/manifest.go)
- 新增 interceptLoop 独立 goroutine:
  (a) cancelLLM() 取消进行中的 HTTP 请求
  (b) interceptCh → drainInterrupt() 注入 [打断消息] 到 LLM 上下文
  (c) InjectInput 空闲时触发新处理循环
- 新增 internal/plugins/all.go 空白导入触发所有内置插件 init()
- internal/sdk/ 作为 PluginSDK 正式 Go API
- internal/api/ → internal/plugins/webui/ 迁移
- 删除旧 cmd/cli/, 使用 cmd/waiter/ 替代
- 更新 PLAN.md / ARCHITECTURE.md / README.md 文档
2026-07-03 16:53:34 +08:00

89 lines
2.0 KiB
Go

package openclaw
import (
"fmt"
"log"
"os"
"path/filepath"
"gitcode.com/JianFeeeee/HomeAgent/internal/plugin"
sdk "gitcode.com/JianFeeeee/HomeAgent/internal/sdk"
)
// SkillsDir 由 main.go 在 Load() 前设置,指向 SKILL.md 存放目录。
var SkillsDir string
func init() {
plugin.RegisterFactory("openclaw", func(name string, config map[string]interface{}) (sdk.Plugin, error) {
dir := SkillsDir
if dir == "" {
dir = filepath.Join(config["data_dir"].(string), "skills")
}
return New(name, dir), nil
})
}
type Plugin struct {
name string
skillsDir string
skills []*plugin.SKILLPlugin
}
func New(name, skillsDir string) *Plugin {
return &Plugin{
name: name,
skillsDir: skillsDir,
}
}
func (p *Plugin) Name() string { return p.name }
func (p *Plugin) Start(s *sdk.PluginSDK) error {
entries, err := os.ReadDir(p.skillsDir)
if err != nil {
if os.IsNotExist(err) {
return nil
}
return fmt.Errorf("read skills dir %s: %w", p.skillsDir, err)
}
for _, entry := range entries {
skillPath := filepath.Join(p.skillsDir, entry.Name())
sk, err := plugin.LoadSKILL(skillPath)
if err != nil {
log.Printf("[openclaw] load skill %s: %v", entry.Name(), err)
continue
}
p.skills = append(p.skills, sk)
// Register each tool defined in the SKILL
for _, td := range sk.Tools() {
name := td.Name
def := sdk.ToolDef{
Name: name,
Description: td.Description,
Parameters: td.Parameters,
}
// SKILL tools are informational (advisory) — no handler
if err := s.RegisterTool(name, def, nil); err != nil {
log.Printf("[openclaw] register tool %s: %v", name, err)
}
}
// Register IO config as a channel if defined
if iocfg := sk.IOConfig(); iocfg != nil {
log.Printf("[openclaw] skill %s io: type=%s in=%s out=%s caps=%v",
sk.Name(), iocfg.Type, iocfg.InputRoute, iocfg.OutputRoute, iocfg.OutputCaps)
}
log.Printf("[openclaw] loaded skill: %s v%s", sk.Name(), sk.Version())
}
return nil
}
func (p *Plugin) Stop() error {
p.skills = nil
return nil
}