mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-10-01 15:02:53 +00:00
feat(sdk): add RegisterStopHandler/RunStopHandlers and wire stop cleanup into registry lifecycle
- SDK PluginSDK gains RegisterStopHandler(fn func()) + RunStopHandlers() (LIFO, idempotent, cleared after running); synced to third_party copy - Registry keeps per-plugin SDK refs (sdkRefs); StopAll/ReloadOne/ DisablePlugin run handlers before calling Stop() - timer built-in plugin demonstrates handler-based shutdown cleanup - z_bridge (linux/windows templates) runs handlers before plugin.Stop()
This commit is contained in:
@ -263,9 +263,13 @@ code { font-family:monospace; font-size:12px; color:var(--pre-color) }
|
|||||||
.msg { max-width:95% }
|
.msg { max-width:95% }
|
||||||
}
|
}
|
||||||
@media(max-width:768px) {
|
@media(max-width:768px) {
|
||||||
nav { padding:0 8px; gap:2px }
|
nav { padding:0 6px; gap:2px; overflow-x:auto; scrollbar-width:none; -ms-overflow-style:none; flex-wrap:nowrap }
|
||||||
nav h1 { font-size:13px; margin-right:8px }
|
nav::-webkit-scrollbar { display:none }
|
||||||
nav a { padding:8px 6px; font-size:11px }
|
nav h1 { display:none }
|
||||||
|
nav a { padding:10px 8px; font-size:12px; white-space:nowrap; flex-shrink:0 }
|
||||||
|
nav > div { flex-shrink:0 }
|
||||||
|
#conn-name-display { display:none }
|
||||||
|
.conn-indicator { padding:4px 6px }
|
||||||
.container { padding:12px }
|
.container { padding:12px }
|
||||||
.card { padding:12px }
|
.card { padding:12px }
|
||||||
.grid-2,.grid-3,.grid-4 { grid-template-columns:1fr }
|
.grid-2,.grid-3,.grid-4 { grid-template-columns:1fr }
|
||||||
@ -284,6 +288,12 @@ code { font-family:monospace; font-size:12px; color:var(--pre-color) }
|
|||||||
.health-item { flex-wrap:wrap; gap:4px }
|
.health-item { flex-wrap:wrap; gap:4px }
|
||||||
.health-item .check-name { flex:auto; width:100% }
|
.health-item .check-name { flex:auto; width:100% }
|
||||||
}
|
}
|
||||||
|
@media(max-width:480px) {
|
||||||
|
.chat-layout { gap:10px }
|
||||||
|
.chat-sidebar { min-width:0 }
|
||||||
|
.msg { max-width:98% }
|
||||||
|
.settings-sidebar a { padding:6px 10px; font-size:12px }
|
||||||
|
}
|
||||||
|
|
||||||
/* Connection Manager */
|
/* Connection Manager */
|
||||||
.overlay { position:fixed; inset:0; background:rgba(0,0,0,0.6); display:none; align-items:center; justify-content:center; z-index:1000 }
|
.overlay { position:fixed; inset:0; background:rgba(0,0,0,0.6); display:none; align-items:center; justify-content:center; z-index:1000 }
|
||||||
|
|||||||
@ -394,7 +394,6 @@ func (r *ConfigRegistry) seedDBValues(dataDir string) {
|
|||||||
set("core.memory.text", filepath.Join(dataDir, "memory", "text"))
|
set("core.memory.text", filepath.Join(dataDir, "memory", "text"))
|
||||||
set("core.memory.documents", filepath.Join(dataDir, "memory", "documents"))
|
set("core.memory.documents", filepath.Join(dataDir, "memory", "documents"))
|
||||||
set("core.knowledge.path", filepath.Join(dataDir, "knowledge"))
|
set("core.knowledge.path", filepath.Join(dataDir, "knowledge"))
|
||||||
set("core.skills.path", filepath.Join(dataDir, "skills"))
|
|
||||||
set("core.log.path", filepath.Join(dataDir, "log"))
|
set("core.log.path", filepath.Join(dataDir, "log"))
|
||||||
|
|
||||||
set("core.agent.max_tool_turns", "10")
|
set("core.agent.max_tool_turns", "10")
|
||||||
@ -488,7 +487,6 @@ func (r *ConfigRegistry) seedCoreDefs(dataDir string) {
|
|||||||
reg(ConfigDef{Key: "core.memory.text", Default: filepath.Join(dataDir, "memory", "text"), Type: "string", DisplayName: "文本记忆路径", Description: "短期文本记忆存储目录", Category: "paths"})
|
reg(ConfigDef{Key: "core.memory.text", Default: filepath.Join(dataDir, "memory", "text"), Type: "string", DisplayName: "文本记忆路径", Description: "短期文本记忆存储目录", Category: "paths"})
|
||||||
reg(ConfigDef{Key: "core.memory.documents", Default: filepath.Join(dataDir, "memory", "documents"), Type: "string", DisplayName: "文档记忆路径", Description: "文档记忆存储目录", Category: "paths"})
|
reg(ConfigDef{Key: "core.memory.documents", Default: filepath.Join(dataDir, "memory", "documents"), Type: "string", DisplayName: "文档记忆路径", Description: "文档记忆存储目录", Category: "paths"})
|
||||||
reg(ConfigDef{Key: "core.knowledge.path", Default: filepath.Join(dataDir, "knowledge"), Type: "string", DisplayName: "知识库路径", Description: "知识库存储目录", Category: "paths"})
|
reg(ConfigDef{Key: "core.knowledge.path", Default: filepath.Join(dataDir, "knowledge"), Type: "string", DisplayName: "知识库路径", Description: "知识库存储目录", Category: "paths"})
|
||||||
reg(ConfigDef{Key: "core.skills.path", Default: filepath.Join(dataDir, "skills"), Type: "string", DisplayName: "技能目录", Description: "OpenClaw 技能存储目录", Category: "paths"})
|
|
||||||
reg(ConfigDef{Key: "core.log.path", Default: filepath.Join(dataDir, "log"), Type: "string", DisplayName: "日志目录", Description: "日志文件输出目录", Category: "paths"})
|
reg(ConfigDef{Key: "core.log.path", Default: filepath.Join(dataDir, "log"), Type: "string", DisplayName: "日志目录", Description: "日志文件输出目录", Category: "paths"})
|
||||||
|
|
||||||
reg(ConfigDef{Key: "core.agent.max_tool_turns", Default: "10", Type: "int", DisplayName: "最大工具轮次", Description: "单次请求允许的最大工具调用轮数", Category: "agent"})
|
reg(ConfigDef{Key: "core.agent.max_tool_turns", Default: "10", Type: "int", DisplayName: "最大工具轮次", Description: "单次请求允许的最大工具调用轮数", Category: "agent"})
|
||||||
|
|||||||
@ -68,6 +68,7 @@ type Registry struct {
|
|||||||
factories map[string]NativeFactory
|
factories map[string]NativeFactory
|
||||||
|
|
||||||
pluginAutoRestart map[string]bool
|
pluginAutoRestart map[string]bool
|
||||||
|
sdkRefs map[string]*sdk.PluginSDK
|
||||||
|
|
||||||
iom *agentIO.IOManager
|
iom *agentIO.IOManager
|
||||||
evBus *events.Bus
|
evBus *events.Bus
|
||||||
@ -103,6 +104,7 @@ func NewRegistry() *Registry {
|
|||||||
plugins: make(map[string]sdk.Plugin),
|
plugins: make(map[string]sdk.Plugin),
|
||||||
factories: make(map[string]NativeFactory),
|
factories: make(map[string]NativeFactory),
|
||||||
pluginAutoRestart: make(map[string]bool),
|
pluginAutoRestart: make(map[string]bool),
|
||||||
|
sdkRefs: make(map[string]*sdk.PluginSDK),
|
||||||
knownDisabled: make(map[string]bool),
|
knownDisabled: make(map[string]bool),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -378,16 +380,25 @@ func (r *Registry) loadOne(plgDir, name string) bool {
|
|||||||
r.mu.Lock()
|
r.mu.Lock()
|
||||||
r.plugins[name] = plg
|
r.plugins[name] = plg
|
||||||
r.pluginAutoRestart[name] = plgSDK.AutoRestart()
|
r.pluginAutoRestart[name] = plgSDK.AutoRestart()
|
||||||
|
r.sdkRefs[name] = plgSDK
|
||||||
r.instances = append(r.instances, plg)
|
r.instances = append(r.instances, plg)
|
||||||
r.mu.Unlock()
|
r.mu.Unlock()
|
||||||
log.Printf("[plugin] loaded: %s", name)
|
log.Printf("[plugin] loaded: %s", name)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// runStopHandlers 执行插件注册的停止清理回调(SDK 层),须在调用插件 Stop() 之前执行。
|
||||||
|
func (r *Registry) runStopHandlers(name string) {
|
||||||
|
if sdk, ok := r.sdkRefs[name]; ok {
|
||||||
|
sdk.RunStopHandlers()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (r *Registry) StopAll() {
|
func (r *Registry) StopAll() {
|
||||||
r.mu.Lock()
|
r.mu.Lock()
|
||||||
defer r.mu.Unlock()
|
defer r.mu.Unlock()
|
||||||
for _, p := range r.instances {
|
for _, p := range r.instances {
|
||||||
|
r.runStopHandlers(p.Name())
|
||||||
if err := p.Stop(); err != nil {
|
if err := p.Stop(); err != nil {
|
||||||
log.Printf("[plugin] stop %s: %v", p.Name(), err)
|
log.Printf("[plugin] stop %s: %v", p.Name(), err)
|
||||||
}
|
}
|
||||||
@ -395,6 +406,7 @@ func (r *Registry) StopAll() {
|
|||||||
r.plugins = make(map[string]sdk.Plugin)
|
r.plugins = make(map[string]sdk.Plugin)
|
||||||
r.instances = nil
|
r.instances = nil
|
||||||
r.pluginAutoRestart = make(map[string]bool)
|
r.pluginAutoRestart = make(map[string]bool)
|
||||||
|
r.sdkRefs = make(map[string]*sdk.PluginSDK)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Registry) Reload(dir string) (string, error) {
|
func (r *Registry) Reload(dir string) (string, error) {
|
||||||
@ -410,10 +422,12 @@ func (r *Registry) ReloadOne(name string) error {
|
|||||||
|
|
||||||
r.mu.Lock()
|
r.mu.Lock()
|
||||||
if p, ok := r.plugins[name]; ok {
|
if p, ok := r.plugins[name]; ok {
|
||||||
|
r.runStopHandlers(name)
|
||||||
if err := p.Stop(); err != nil {
|
if err := p.Stop(); err != nil {
|
||||||
log.Printf("[plugin] stop %s for reload: %v", name, err)
|
log.Printf("[plugin] stop %s for reload: %v", name, err)
|
||||||
}
|
}
|
||||||
delete(r.plugins, name)
|
delete(r.plugins, name)
|
||||||
|
delete(r.sdkRefs, name)
|
||||||
for i, inst := range r.instances {
|
for i, inst := range r.instances {
|
||||||
if inst.Name() == name {
|
if inst.Name() == name {
|
||||||
r.instances = append(r.instances[:i], r.instances[i+1:]...)
|
r.instances = append(r.instances[:i], r.instances[i+1:]...)
|
||||||
@ -481,10 +495,12 @@ func (r *Registry) Disable(name string) error {
|
|||||||
r.mu.Lock()
|
r.mu.Lock()
|
||||||
p, ok := r.plugins[name]
|
p, ok := r.plugins[name]
|
||||||
if ok {
|
if ok {
|
||||||
|
r.runStopHandlers(name)
|
||||||
if err := p.Stop(); err != nil {
|
if err := p.Stop(); err != nil {
|
||||||
log.Printf("[plugin] stop %s for disable: %v", name, err)
|
log.Printf("[plugin] stop %s for disable: %v", name, err)
|
||||||
}
|
}
|
||||||
delete(r.plugins, name)
|
delete(r.plugins, name)
|
||||||
|
delete(r.sdkRefs, name)
|
||||||
for i, inst := range r.instances {
|
for i, inst := range r.instances {
|
||||||
if inst.Name() == name {
|
if inst.Name() == name {
|
||||||
r.instances = append(r.instances[:i], r.instances[i+1:]...)
|
r.instances = append(r.instances[:i], r.instances[i+1:]...)
|
||||||
@ -539,10 +555,12 @@ func (r *Registry) DisablePlugin(name, by string) error {
|
|||||||
r.mu.Lock()
|
r.mu.Lock()
|
||||||
p, ok := r.plugins[name]
|
p, ok := r.plugins[name]
|
||||||
if ok {
|
if ok {
|
||||||
|
r.runStopHandlers(name)
|
||||||
if err := p.Stop(); err != nil {
|
if err := p.Stop(); err != nil {
|
||||||
log.Printf("[plugin] stop %s for disable: %v", name, err)
|
log.Printf("[plugin] stop %s for disable: %v", name, err)
|
||||||
}
|
}
|
||||||
delete(r.plugins, name)
|
delete(r.plugins, name)
|
||||||
|
delete(r.sdkRefs, name)
|
||||||
for i, inst := range r.instances {
|
for i, inst := range r.instances {
|
||||||
if inst.Name() == name {
|
if inst.Name() == name {
|
||||||
r.instances = append(r.instances[:i], r.instances[i+1:]...)
|
r.instances = append(r.instances[:i], r.instances[i+1:]...)
|
||||||
|
|||||||
@ -42,6 +42,8 @@ func (p *Plugin) Name() string { return p.name }
|
|||||||
func (p *Plugin) Start(s *sdk.PluginSDK) error {
|
func (p *Plugin) Start(s *sdk.PluginSDK) error {
|
||||||
s.SetAutoRestart(true)
|
s.SetAutoRestart(true)
|
||||||
p.maxDur = 24 * time.Hour
|
p.maxDur = 24 * time.Hour
|
||||||
|
// 停止清理(取消倒计时)交由 stop handler:内核在调用 Stop() 之前执行。
|
||||||
|
s.RegisterStopHandler(func() { close(p.stopCh) })
|
||||||
s.Settings().RegisterDef(sdk.ConfigDef{
|
s.Settings().RegisterDef(sdk.ConfigDef{
|
||||||
Key: "max_duration", Type: "string", DisplayName: "最大定时时长",
|
Key: "max_duration", Type: "string", DisplayName: "最大定时时长",
|
||||||
Description: "允许设置的最大定时时长,例如 24h, 7d, 1h(默认 24h)",
|
Description: "允许设置的最大定时时长,例如 24h, 7d, 1h(默认 24h)",
|
||||||
@ -118,7 +120,6 @@ func (p *Plugin) Start(s *sdk.PluginSDK) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *Plugin) Stop() error {
|
func (p *Plugin) Stop() error {
|
||||||
close(p.stopCh)
|
|
||||||
p.wg.Wait()
|
p.wg.Wait()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -258,9 +258,11 @@ code { font-family:monospace; font-size:12px; color:var(--pre-color) }
|
|||||||
.msg { max-width:95% }
|
.msg { max-width:95% }
|
||||||
}
|
}
|
||||||
@media(max-width:768px) {
|
@media(max-width:768px) {
|
||||||
nav { padding:0 8px; gap:2px }
|
nav { padding:0 6px; gap:2px; overflow-x:auto; scrollbar-width:none; -ms-overflow-style:none; flex-wrap:nowrap }
|
||||||
nav h1 { font-size:13px; margin-right:8px }
|
nav::-webkit-scrollbar { display:none }
|
||||||
nav a { padding:8px 6px; font-size:11px }
|
nav h1 { font-size:0; margin-right:6px; color:var(--accent) }
|
||||||
|
nav a { padding:10px 8px; font-size:12px; white-space:nowrap; flex-shrink:0 }
|
||||||
|
nav > div { flex-shrink:0 }
|
||||||
.container { padding:12px }
|
.container { padding:12px }
|
||||||
.card { padding:12px }
|
.card { padding:12px }
|
||||||
.grid-2,.grid-3,.grid-4 { grid-template-columns:1fr }
|
.grid-2,.grid-3,.grid-4 { grid-template-columns:1fr }
|
||||||
|
|||||||
28
third_party/homeagent-sdk/sdk/plugin.go
vendored
28
third_party/homeagent-sdk/sdk/plugin.go
vendored
@ -199,6 +199,9 @@ type PluginSDK struct {
|
|||||||
events EventSubscriber
|
events EventSubscriber
|
||||||
|
|
||||||
autoRestart bool
|
autoRestart bool
|
||||||
|
|
||||||
|
stopMu sync.Mutex
|
||||||
|
stopHandlers []func()
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a PluginSDK with the given dependencies.
|
// New creates a PluginSDK with the given dependencies.
|
||||||
@ -367,3 +370,28 @@ func (s *PluginSDK) SetAutoRestart(enabled bool) { s.autoRestart = enabled }
|
|||||||
|
|
||||||
// AutoRestart 返回插件是否允许自动重启。
|
// AutoRestart 返回插件是否允许自动重启。
|
||||||
func (s *PluginSDK) AutoRestart() bool { return s.autoRestart }
|
func (s *PluginSDK) AutoRestart() bool { return s.autoRestart }
|
||||||
|
|
||||||
|
// RegisterStopHandler 注册插件停止阶段的清理回调。
|
||||||
|
// 注册的 handler 会在插件 Stop() 之前按"后注册先执行"的顺序调用,
|
||||||
|
// 适用于释放资源、落盘状态、关闭子进程等停止时清理操作。
|
||||||
|
// 可注册多个;执行后清空(进程停止前只执行一次)。
|
||||||
|
func (s *PluginSDK) RegisterStopHandler(fn func()) {
|
||||||
|
if fn == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.stopMu.Lock()
|
||||||
|
s.stopHandlers = append(s.stopHandlers, fn)
|
||||||
|
s.stopMu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunStopHandlers 执行全部已注册的 stop handler(后注册先执行,执行后清空,幂等)。
|
||||||
|
// 由内核(内置插件)或插件桥接层(外部插件 z_bridge 的 StopPlugin)在调用插件 Stop() 前执行。
|
||||||
|
func (s *PluginSDK) RunStopHandlers() {
|
||||||
|
s.stopMu.Lock()
|
||||||
|
handlers := append([]func(){}, s.stopHandlers...)
|
||||||
|
s.stopHandlers = nil
|
||||||
|
s.stopMu.Unlock()
|
||||||
|
for i := len(handlers) - 1; i >= 0; i-- {
|
||||||
|
handlers[i]()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user