feat: plugin discovery, capability/contribution/permission registries, Plugin Manager UI
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
// Package capability provides a registry for plugin capabilities.
|
||||
package capability
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Registry tracks available capabilities and which plugins provide them.
|
||||
type Registry struct {
|
||||
mu sync.RWMutex
|
||||
capabilities map[string]*Entry // capability name -> entry
|
||||
}
|
||||
|
||||
// Entry represents a capability and its provider.
|
||||
type Entry struct {
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description,omitempty"`
|
||||
PluginID string `json:"pluginId"`
|
||||
Status string `json:"status"` // "stable", "draft", "deprecated"
|
||||
}
|
||||
|
||||
// NewRegistry creates a new capability registry.
|
||||
func NewRegistry() *Registry {
|
||||
return &Registry{
|
||||
capabilities: make(map[string]*Entry),
|
||||
}
|
||||
}
|
||||
|
||||
// Register adds a capability provided by a plugin.
|
||||
func (r *Registry) Register(pluginID string, capabilities []string) error {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
for _, name := range capabilities {
|
||||
if existing, ok := r.capabilities[name]; ok {
|
||||
return fmt.Errorf("capability %q already registered by plugin %q", name, existing.PluginID)
|
||||
}
|
||||
r.capabilities[name] = &Entry{
|
||||
Name: name,
|
||||
PluginID: pluginID,
|
||||
Status: "draft",
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Unregister removes all capabilities provided by a plugin.
|
||||
func (r *Registry) Unregister(pluginID string) {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
for name, entry := range r.capabilities {
|
||||
if entry.PluginID == pluginID {
|
||||
delete(r.capabilities, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Has checks if a capability is registered.
|
||||
func (r *Registry) Has(name string) bool {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
_, ok := r.capabilities[name]
|
||||
return ok
|
||||
}
|
||||
|
||||
// Get returns a capability entry by name.
|
||||
func (r *Registry) Get(name string) *Entry {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
return r.capabilities[name]
|
||||
}
|
||||
|
||||
// List returns all registered capabilities, sorted by name.
|
||||
func (r *Registry) List() []Entry {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
|
||||
entries := make([]Entry, 0, len(r.capabilities))
|
||||
for _, e := range r.capabilities {
|
||||
entries = append(entries, *e)
|
||||
}
|
||||
sort.Slice(entries, func(i, j int) bool {
|
||||
return entries[i].Name < entries[j].Name
|
||||
})
|
||||
return entries
|
||||
}
|
||||
|
||||
// Available returns the set of available capability names.
|
||||
func (r *Registry) Available() map[string]bool {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
|
||||
result := make(map[string]bool, len(r.capabilities))
|
||||
for name := range r.capabilities {
|
||||
result[name] = true
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// CheckRequired verifies that all required capabilities are present.
|
||||
// Returns missing capabilities.
|
||||
func (r *Registry) CheckRequired(required []string) []string {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
|
||||
var missing []string
|
||||
for _, capName := range required {
|
||||
if _, ok := r.capabilities[capName]; !ok {
|
||||
missing = append(missing, capName)
|
||||
}
|
||||
}
|
||||
return missing
|
||||
}
|
||||
Reference in New Issue
Block a user