Documentation
¶
Overview ¶
Package plugin defines the unified plugin host for huan extensions.
Plugin is the minimal base interface every plugin satisfies. All base types are aliased from pkg/plugin so .so plugins and internal code share the same type identity. EventSubscriber stays defined here because it references internal eventbus types.
Capability interfaces (e.g. deploy.Deployer) embed Plugin and add domain- specific methods. The Registry holds plugins keyed by Name(); Find[T] returns the subset implementing a given capability.
See docs/adr/0003-unified-plugin-system.md for the architectural decisions.
Index ¶
- Constants
- Variables
- func Find[T any](r *Registry) []T
- func HuanHome() string
- func ShouldLoadInCategory(pluginCategory, mode string) bool
- func SoFileName(name string) string
- func ValidateConfig(name string, schema Schema, raw map[string]any) []string
- func ValidateRawConfigs(registry *Registry, rawConfigs map[string]config.PluginConfig, ...) (errors, warnings []string)
- type EventSubscriber
- type FieldSchema
- type GRPCPlugin
- type GRPCStub
- type LifecycleManager
- func (m *LifecycleManager) List() []PluginInfo
- func (m *LifecycleManager) Load(soPath string, pluginCfg map[string]any) (Plugin, error)
- func (m *LifecycleManager) Reload(name string, newSO string, pluginCfg map[string]any) error
- func (m *LifecycleManager) SetCapabilityDetector(fn func(Plugin) string)
- func (m *LifecycleManager) Start(ctx context.Context) error
- func (m *LifecycleManager) Stop()
- func (m *LifecycleManager) Unload(name string) error
- type Loader
- func (l *Loader) LoadPlugin(path string, pluginCfg map[string]any) (Plugin, error)
- func (l *Loader) PluginDir() string
- func (l *Loader) Resolve(soFile string) string
- func (l *Loader) ScanAndLoad() ([]ScanAndLoadResult, error)
- func (l *Loader) ScanAndLoadByCategory(pluginConfigs map[string]config.PluginConfig, categories ...string) ([]ScanAndLoadResult, error)
- type MetadataProvider
- type Plugin
- type PluginInfo
- type PluginInitFunc
- type PluginMeta
- type PluginWatcher
- type Registry
- type ScanAndLoadResult
- type Schema
- type SchemaProvider
Constants ¶
const ( CategoryStatic = "static" CategoryDynamic = "dynamic" CategoryMixed = "mixed" )
Variables ¶
var ( ErrMissingInitSymbol = errors.New("plugin: missing InitPlugin symbol") ErrPluginNameConflict = errors.New("plugin: name already registered") )
var ErrGRPCNotImplemented = errors.New("plugin: gRPC not implemented yet")
ErrGRPCNotImplemented is returned by GRPCStub methods until the gRPC transport layer is actually implemented.
var ErrPluginNotFound = fmt.Errorf("plugin: not found")
Functions ¶
func Find ¶
Find returns all registered plugins implementing capability T, in registration order. T is typically a capability interface.
func HuanHome ¶ added in v0.7.0
func HuanHome() string
HuanHome returns the global huan home directory used for centrally-installed plugins. It honors the $HUAN_HOME environment variable, falling back to ~/.huan. Returns "" only when neither $HUAN_HOME nor a home directory can be determined.
func ShouldLoadInCategory ¶ added in v0.7.0
ShouldLoadInCategory 判断给定 category 的插件是否应在当前 mode 下加载。 mode 为 "build" 或 "daemon"。
func SoFileName ¶ added in v0.7.0
SoFileName returns the conventional .so file name for a plugin whose config key / Name() is `name`. huan.yaml plugin keys use underscores (e.g. qwen3_translate); the .so files on disk use hyphens (e.g. qwen3-translate.so). Callers derive filenames from config keys via this helper instead of hardcoding plugin file names.
func ValidateConfig ¶ added in v0.7.0
ValidateConfig checks raw config against the schema. Returns a list of validation errors (empty = valid). Each error is a human-readable string. Unknown fields in raw produce warnings (prefixed with "WARN:"). Missing required fields produce errors. Type mismatches produce errors.
func ValidateRawConfigs ¶ added in v0.7.0
func ValidateRawConfigs(registry *Registry, rawConfigs map[string]config.PluginConfig, soExists func(name string) bool) (errors, warnings []string)
ValidateRawConfigs validates all plugin configs against their schemas. Returns errors and warnings separately. Plugins that don't implement SchemaProvider are skipped.
soExists reports whether a plugin's .so file is resolvable on disk (e.g. via Loader.Resolve). A plugin declared in yaml but absent from `registry` is not necessarily a problem: build-stage registries deliberately exclude dynamic plugins (deploy/translate/…), which the daemon or a command loads from their .so at runtime. So a missing-from-registry plugin only warrants a warning when its .so is genuinely absent. When soExists is nil, every missing-from-registry plugin is treated as having no .so (warn).
Types ¶
type EventSubscriber ¶ added in v0.7.0
type EventSubscriber interface {
// SubscribedEvents returns the event types this plugin wants to receive.
// Return nil or empty slice to skip all events.
SubscribedEvents() []eventbus.EventType
// HandleEvent is called for each subscribed event. Returning an error
// logs the failure but does not interrupt other handlers.
HandleEvent(ctx context.Context, event eventbus.Event) error
}
EventSubscriber is an optional interface plugins can implement to subscribe to system events. The LifecycleManager registers these subscriptions when the plugin is loaded (compiled or .so) in serve/dev mode.
This interface stays in internal/plugin because it references internal daemon/eventbus types that are not exported from pkg/plugin.
type FieldSchema ¶ added in v0.7.0
type FieldSchema = pkgplugin.FieldSchema
type GRPCPlugin ¶ added in v0.7.0
type GRPCPlugin interface {
Plugin
// Capability returns the plugin's capability type (e.g. "deployer",
// "translator", "seo_checker").
Capability() string
// Call invokes a remote method on the plugin.
// Currently returns ErrGRPCNotImplemented.
Call(ctx context.Context, method string, payload []byte) ([]byte, error)
// Health checks whether the remote plugin is alive.
Health(ctx context.Context) error
}
GRPCPlugin defines the interface for plugins that communicate via gRPC. This is a reserved interface for future use — the gRPC transport layer will be implemented when cross-language plugin support is needed.
type GRPCStub ¶ added in v0.7.0
type GRPCStub struct {
// contains filtered or unexported fields
}
GRPCStub is a placeholder for future gRPC-based plugins. It implements GRPCPlugin with stub methods that return ErrGRPCNotImplemented. The actual gRPC client will be implemented later in internal/plugin/grpc/.
func NewGRPCStub ¶ added in v0.7.0
NewGRPCStub creates a new GRPCStub. All methods return stub values until the gRPC transport layer is implemented.
func (*GRPCStub) Capability ¶ added in v0.7.0
type LifecycleManager ¶ added in v0.7.0
type LifecycleManager struct {
// contains filtered or unexported fields
}
LifecycleManager manages the complete lifecycle of plugins: discovery, loading, unloading, reloading, and event publishing.
func NewLifecycleManager ¶ added in v0.7.0
func NewLifecycleManager(registry *Registry, loader *Loader, bus eventbus.EventBus) *LifecycleManager
NewLifecycleManager creates a LifecycleManager.
func (*LifecycleManager) List ¶ added in v0.7.0
func (m *LifecycleManager) List() []PluginInfo
List returns metadata about all registered plugins (compiled + loaded).
func (*LifecycleManager) Load ¶ added in v0.7.0
Load loads a .so plugin from the given path, registers it, and publishes an event. Returns ErrPluginNameConflict if a plugin with the same name already exists. Returns an error if the path is outside the plugin directory. The pluginCfg map is passed to the plugin's InitPlugin function.
func (*LifecycleManager) Reload ¶ added in v0.7.0
Reload replaces a loaded plugin's implementation by loading a new .so. If the new .so fails to load, the original plugin is preserved (rollback). Returns ErrPluginNotFound if the plugin is not registered. The pluginCfg map is passed to the plugin's InitPlugin function.
func (*LifecycleManager) SetCapabilityDetector ¶ added in v0.7.0
func (m *LifecycleManager) SetCapabilityDetector(fn func(Plugin) string)
SetCapabilityDetector registers a function that returns capability labels for a given plugin. The composition root (cmd/huan) should set this to enable Admin API and CLI plugin list to show capability info.
The detector receives the full plugin.Plugin interface and can use type assertions against domain capability interfaces (deploy.Deployer, etc.). It is called from List() and from the Admin API.
func (*LifecycleManager) Start ¶ added in v0.7.0
func (m *LifecycleManager) Start(ctx context.Context) error
Start discovers and loads all .so plugins from the plugin directory, then starts the file watcher for hot-reload. Already-registered compiled plugins are tracked but not re-loaded.
func (*LifecycleManager) Stop ¶ added in v0.7.0
func (m *LifecycleManager) Stop()
Stop unloads all runtime-loaded plugins. Does not remove compiled plugins.
func (*LifecycleManager) Unload ¶ added in v0.7.0
func (m *LifecycleManager) Unload(name string) error
Unload removes a plugin by name. Returns ErrPluginNotFound if the plugin is not registered. Does NOT remove compiled plugins.
type Loader ¶ added in v0.7.0
type Loader struct {
// contains filtered or unexported fields
}
Loader discovers and loads .so plugin files from a directory.
func (*Loader) LoadPlugin ¶ added in v0.7.0
LoadPlugin opens a .so file, finds the InitPlugin symbol, and calls it. Returns the Plugin instance or an error. The pluginCfg map is passed to the plugin's InitPlugin function, allowing configuration from huan.yaml.
func (*Loader) PluginDir ¶ added in v0.7.0
PluginDir returns the project-level plugin directory path. This is the directory the daemon watches for hot-reload and validates reload paths against; it does NOT include $HUAN_HOME (see searchDirs).
func (*Loader) Resolve ¶ added in v0.7.0
Resolve returns the filesystem path to a plugin .so identified by its base file name (e.g. "cloudflare.so"), searching $HUAN_HOME first then the project plugin dir. It returns "" when the file exists in none of them.
func (*Loader) ScanAndLoad ¶ added in v0.7.0
func (l *Loader) ScanAndLoad() ([]ScanAndLoadResult, error)
ScanAndLoad scans the pluginDir for all .so files, loads each one, and returns the successfully loaded plugins with their paths. Files that fail to load are skipped with a warning (logged to stderr). Returns an error only if the pluginDir cannot be read.
func (*Loader) ScanAndLoadByCategory ¶ added in v0.7.0
func (l *Loader) ScanAndLoadByCategory(pluginConfigs map[string]config.PluginConfig, categories ...string) ([]ScanAndLoadResult, error)
ScanAndLoadByCategory scans all .so files in the plugin directory, loads each one, and returns only those whose category (from config) matches one of the given categories.
type MetadataProvider ¶ added in v0.7.0
type MetadataProvider = pkgplugin.MetadataProvider
type Plugin ¶
All base types are aliased from pkg/plugin so .so plugins and huan internal code share the same type identity.
type PluginInfo ¶ added in v0.7.0
type PluginInfo struct {
Name string `json:"name"`
Version string `json:"version"`
Source string `json:"source"` // "compiled" | "loaded" | "grpc"
Capability string `json:"capability,omitempty"`
Status string `json:"status"` // "active" | "inactive" | "error"
LoadedAt string `json:"loadedAt,omitempty"`
Error string `json:"error,omitempty"`
// 新增元数据字段
Author string `json:"author,omitempty"`
RepoURL string `json:"repoURL,omitempty"`
License string `json:"license,omitempty"`
Tags []string `json:"tags,omitempty"`
}
PluginInfo is the metadata returned by LifecycleManager.List() and used by the Admin API and CLI for display.
type PluginInitFunc ¶ added in v0.7.0
PluginInitFunc is the exported symbol every .so plugin must define. The function receives the plugin's raw config and returns a Plugin instance.
type PluginMeta ¶ added in v0.7.0
type PluginMeta = pkgplugin.PluginMeta
type PluginWatcher ¶ added in v0.7.0
type PluginWatcher struct {
// contains filtered or unexported fields
}
PluginWatcher monitors the plugin directory for new, modified, or deleted .so files and triggers hot-reload through the LifecycleManager.
func NewPluginWatcher ¶ added in v0.7.0
func NewPluginWatcher(dir string, manager *LifecycleManager, logf func(string, ...any)) *PluginWatcher
NewPluginWatcher creates a PluginWatcher that notifies the given manager of filesystem changes in dir.
func (*PluginWatcher) Start ¶ added in v0.7.0
func (w *PluginWatcher) Start(ctx context.Context) error
Start begins watching the plugin directory for .so file changes. It uses fsnotify to trigger automatic Load/Unload/Reload operations. A debounce of 500ms prevents duplicate events. Returns nil when the context is cancelled.
type Registry ¶
Registry is aliased from pkg/plugin — struct with all methods (Register, Get, All, Unregister, Names, SortedNames) defined there.
type ScanAndLoadResult ¶ added in v0.7.0
ScanAndLoadResult pairs a loaded plugin with its .so filesystem path.
type SchemaProvider ¶ added in v0.7.0
type SchemaProvider = pkgplugin.SchemaProvider