Files
MuyueWorkspace/internal/plugins/plugin.go
Augustin 4523bbd42c
All checks were successful
Stable Release / stable (push) Successful in 1m34s
feat: RAG, memory, plugins, lessons, file editor, split panes, Markdown rendering, PWA + UI overhaul
Major additions:
- RAG pipeline (indexing, chunking, search) with sidebar upload button
- Memory system with CRUD API
- Plugins and lessons modules
- MCP discovery and MCP server
- Advanced skills (auto-create, conditional, improver)
- Agent browser/image support, delegate, sessions
- File editor with CodeMirror in split panes
- Markdown rendering via react-markdown + KaTeX + highlight.js
- Raw markdown toggle
- PWA manifest + service worker
- Extension UI redesign with new design tokens and studio-style chat
- Pipeline API for chat streaming
- Mobile responsive layout

💘 Generated with Crush

Assisted-by: GLM-5.1 via Crush <crush@charm.land>
2026-04-27 21:04:11 +02:00

225 lines
4.9 KiB
Go

package plugins
import (
"context"
"encoding/json"
"fmt"
"sync"
"github.com/muyue/muyue/internal/agent"
)
type PluginStatus string
const (
StatusEnabled PluginStatus = "enabled"
StatusDisabled PluginStatus = "disabled"
StatusError PluginStatus = "error"
)
type Plugin struct {
name string
version string
description string
status PluginStatus
tools []*agent.ToolDefinition
hooks map[HookType]HookFunc
init func(ctx context.Context, registry *agent.Registry) error
}
func NewPlugin(name, version, description string) *Plugin {
return &Plugin{
name: name,
version: version,
description: description,
status: StatusDisabled,
tools: make([]*agent.ToolDefinition, 0),
hooks: make(map[HookType]HookFunc),
}
}
func (p *Plugin) Name() string { return p.name }
func (p *Plugin) Version() string { return p.version }
func (p *Plugin) Description() string { return p.description }
func (p *Plugin) Status() PluginStatus { return p.status }
func (p *Plugin) AddTool(tool *ToolDefinition) *Plugin {
td := &agent.ToolDefinition{
Name: tool.Name,
Description: tool.Description,
Params: tool.Params,
Handler: tool.Handler,
}
p.tools = append(p.tools, td)
return p
}
func (p *Plugin) AddToolGeneric(params interface{}, name, description string, handler func(ctx context.Context, raw json.RawMessage) (agent.ToolResponse, error)) *Plugin {
paramsSchema, err := generatePluginSchema(params)
if err == nil {
td := &agent.ToolDefinition{
Name: name,
Description: description,
Params: paramsSchema,
Handler: handler,
}
p.tools = append(p.tools, td)
}
return p
}
func (p *Plugin) AddHook(hookType HookType, fn HookFunc) *Plugin {
p.hooks[hookType] = fn
return p
}
func (p *Plugin) SetInit(fn func(ctx context.Context, registry *agent.Registry) error) *Plugin {
p.init = fn
return p
}
type ToolDefinition struct {
Name string
Description string
Params json.RawMessage
Handler func(ctx context.Context, args json.RawMessage) (agent.ToolResponse, error)
}
type PluginInfo struct {
Name string `json:"name"`
Version string `json:"version"`
Description string `json:"description"`
Status PluginStatus `json:"status"`
ToolCount int `json:"tool_count"`
HookTypes []string `json:"hook_types,omitempty"`
Error string `json:"error,omitempty"`
}
type Manager struct {
mu sync.RWMutex
plugins map[string]*Plugin
hooks *HookRegistry
enabled map[string]bool
}
func NewManager(hooks *HookRegistry) *Manager {
return &Manager{
plugins: make(map[string]*Plugin),
hooks: hooks,
enabled: make(map[string]bool),
}
}
func (m *Manager) Register(p *Plugin) error {
m.mu.Lock()
defer m.mu.Unlock()
if _, exists := m.plugins[p.name]; exists {
return fmt.Errorf("plugin %q already registered", p.name)
}
m.plugins[p.name] = p
return nil
}
func (m *Manager) Enable(ctx context.Context, name string, registry *agent.Registry) error {
m.mu.Lock()
defer m.mu.Unlock()
p, ok := m.plugins[name]
if !ok {
return fmt.Errorf("plugin %q not found", name)
}
if p.status == StatusEnabled {
return nil
}
if p.init != nil {
if err := p.init(ctx, registry); err != nil {
p.status = StatusError
return fmt.Errorf("plugin %q init failed: %w", name, err)
}
}
for _, tool := range p.tools {
if err := registry.Register(tool); err != nil {
p.status = StatusError
return fmt.Errorf("plugin %q register tool %q: %w", name, tool.Name, err)
}
}
for hookType, fn := range p.hooks {
m.hooks.Register(hookType, name, 10, fn)
}
p.status = StatusEnabled
m.enabled[name] = true
return nil
}
func (m *Manager) Disable(name string) {
m.mu.Lock()
defer m.mu.Unlock()
p, ok := m.plugins[name]
if !ok {
return
}
if p.status != StatusEnabled {
return
}
m.hooks.RemoveByPlugin(name)
p.status = StatusDisabled
delete(m.enabled, name)
}
func (m *Manager) Get(name string) (*Plugin, bool) {
m.mu.RLock()
defer m.mu.RUnlock()
p, ok := m.plugins[name]
return p, ok
}
func (m *Manager) List() []PluginInfo {
m.mu.RLock()
defer m.mu.RUnlock()
result := make([]PluginInfo, 0, len(m.plugins))
for _, p := range m.plugins {
info := PluginInfo{
Name: p.name,
Version: p.version,
Description: p.description,
Status: p.status,
ToolCount: len(p.tools),
}
for ht := range p.hooks {
info.HookTypes = append(info.HookTypes, string(ht))
}
result = append(result, info)
}
return result
}
func (m *Manager) EnabledNames() []string {
m.mu.RLock()
defer m.mu.RUnlock()
names := make([]string, 0, len(m.enabled))
for name := range m.enabled {
names = append(names, name)
}
return names
}
func (m *Manager) EnableFromConfig(ctx context.Context, enabledList []string, registry *agent.Registry) {
for _, name := range enabledList {
if err := m.Enable(ctx, name, registry); err != nil {
_ = err
}
}
}