feat(plugin): enforce api version compatibility at registration

This commit is contained in:
xj 2026-02-22 19:09:12 -08:00
parent 5680f69e8b
commit f84f55f80f
4 changed files with 40 additions and 1 deletions

View file

@ -17,6 +17,10 @@ func (p blockingPlugin) Name() string {
return "block-outbound"
}
func (p blockingPlugin) APIVersion() string {
return plugin.APIVersion
}
func (p blockingPlugin) Register(r *hooks.HookRegistry) error {
r.OnMessageSending("block-all", 0, func(_ context.Context, e *hooks.MessageSendingEvent) error {
e.Cancel = true

View file

@ -8,6 +8,7 @@ import (
"time"
"github.com/sipeed/picoclaw/pkg/hooks"
"github.com/sipeed/picoclaw/pkg/plugin"
)
// PolicyDemoConfig controls the demo plugin behavior.
@ -108,6 +109,10 @@ func (p *PolicyDemoPlugin) Name() string {
return "policy-demo"
}
func (p *PolicyDemoPlugin) APIVersion() string {
return plugin.APIVersion
}
func (p *PolicyDemoPlugin) Snapshot() PolicyDemoStats {
p.mu.Lock()
defer p.mu.Unlock()

View file

@ -22,6 +22,7 @@ const APIVersion = "v1alpha1"
// Plugin is the Phase-1 compile-time contract for PicoClaw extensions.
type Plugin interface {
Name() string
APIVersion() string
Register(*hooks.HookRegistry) error
}
@ -62,6 +63,17 @@ func (m *Manager) Register(p Plugin) error {
if name == "" {
return errors.New("plugin name is required")
}
if got := strings.TrimSpace(p.APIVersion()); got != APIVersion {
if got == "" {
got = "<empty>"
}
return fmt.Errorf(
"plugin %q api version mismatch: got %s, want %s",
name,
got,
APIVersion,
)
}
m.mu.Lock()
defer m.mu.Unlock()
@ -85,4 +97,3 @@ func (m *Manager) RegisterAll(plugins ...Plugin) error {
}
return nil
}

View file

@ -10,6 +10,7 @@ import (
type testPlugin struct {
name string
apiVersion string
registerFn func(*hooks.HookRegistry) error
}
@ -24,6 +25,13 @@ func (p testPlugin) Register(r *hooks.HookRegistry) error {
return nil
}
func (p testPlugin) APIVersion() string {
if p.apiVersion == "" {
return APIVersion
}
return p.apiVersion
}
func TestNewManager(t *testing.T) {
m := NewManager()
if m == nil {
@ -110,3 +118,14 @@ func TestRegisterPropagatesPluginError(t *testing.T) {
}
}
func TestRegisterRejectsPluginVersionMismatch(t *testing.T) {
m := NewManager()
p := testPlugin{
name: "old-plugin",
apiVersion: "v0",
}
err := m.Register(p)
if err == nil {
t.Fatal("expected version mismatch error")
}
}