plugin

package
v0.7.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 28, 2026 License: MIT Imports: 14 Imported by: 0

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

View Source
const (
	CategoryStatic  = "static"
	CategoryDynamic = "dynamic"
	CategoryMixed   = "mixed"
)

Variables

View Source
var (
	ErrMissingInitSymbol  = errors.New("plugin: missing InitPlugin symbol")
	ErrPluginNameConflict = errors.New("plugin: name already registered")
)
View Source
var ErrGRPCNotImplemented = errors.New("plugin: gRPC not implemented yet")

ErrGRPCNotImplemented is returned by GRPCStub methods until the gRPC transport layer is actually implemented.

View Source
var ErrPluginNotFound = fmt.Errorf("plugin: not found")

Functions

func Find

func Find[T any](r *Registry) []T

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

func ShouldLoadInCategory(pluginCategory, mode string) bool

ShouldLoadInCategory 判断给定 category 的插件是否应在当前 mode 下加载。 mode 为 "build" 或 "daemon"。

func SoFileName added in v0.7.0

func SoFileName(name string) string

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

func ValidateConfig(name string, schema Schema, raw map[string]any) []string

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

func NewGRPCStub(name, capability, address string) *GRPCStub

NewGRPCStub creates a new GRPCStub. All methods return stub values until the gRPC transport layer is implemented.

func (*GRPCStub) Call added in v0.7.0

func (s *GRPCStub) Call(_ context.Context, _ string, _ []byte) ([]byte, error)

func (*GRPCStub) Capability added in v0.7.0

func (s *GRPCStub) Capability() string

func (*GRPCStub) Health added in v0.7.0

func (s *GRPCStub) Health(_ context.Context) error

func (*GRPCStub) Name added in v0.7.0

func (s *GRPCStub) Name() string

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

func (m *LifecycleManager) Load(soPath string, pluginCfg map[string]any) (Plugin, error)

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

func (m *LifecycleManager) Reload(name string, newSO string, pluginCfg map[string]any) error

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 NewLoader added in v0.7.0

func NewLoader(pluginDir string) *Loader

NewLoader creates a Loader that scans pluginDir for .so files.

func (*Loader) LoadPlugin added in v0.7.0

func (l *Loader) LoadPlugin(path string, pluginCfg map[string]any) (Plugin, error)

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

func (l *Loader) PluginDir() string

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

func (l *Loader) Resolve(soFile string) string

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

type Plugin = pkgplugin.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

type PluginInitFunc func(cfg map[string]any) (interface{}, error)

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

type Registry = pkgplugin.Registry

Registry is aliased from pkg/plugin — struct with all methods (Register, Get, All, Unregister, Names, SortedNames) defined there.

func NewRegistry

func NewRegistry() *Registry

NewRegistry returns an empty Registry.

type ScanAndLoadResult added in v0.7.0

type ScanAndLoadResult struct {
	Plugin Plugin
	Path   string
}

ScanAndLoadResult pairs a loaded plugin with its .so filesystem path.

type Schema added in v0.7.0

type Schema = pkgplugin.Schema

type SchemaProvider added in v0.7.0

type SchemaProvider = pkgplugin.SchemaProvider

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL