plugin

package
v0.26.1 Latest Latest
Warning

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

Go to latest
Published: Jun 28, 2026 License: MIT Imports: 25 Imported by: 0

Documentation

Overview

Package plugin is the host side of the connector plugin platform: it spawns connector subprocesses on demand, hands out live gRPC clients, and reaps idle ones.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func BuildModule

func BuildModule(mod connector.Module, getConn ConnGetter) connector.Module

BuildModule wires a parsed connector.Module's operations to gRPC closures that dispatch to the plugin subprocess. The host engine (service.Execute) calls these closures exactly like in-proc ops — same pattern as custom-MCP. The envelope parsing and verification happen in the loader before this is called.

func DefaultDir

func DefaultDir() string

DefaultDir is the runtime location wick scans for installed connector plugins: <home>/.wick/plugins/connectors, overridable with WICK_PLUGINS_DIR. It matches the layout `make plugins` writes to.

func ExtractArchive

func ExtractArchive(archive, destBase string) (string, error)

ExtractArchive extracts a .tar.gz or .zip into destBase and returns the directory that holds plugin.json (the archive root or its single subdir).

func InstallFromDir

func InstallFromDir(srcDir, destRoot string) error

InstallFromDir verifies the {binary, plugin.json} in srcDir and copies them into destRoot/<key>/. The manifest's sha256 + signature are checked against the binary BEFORE anything is written, so an unverified plugin never lands in the plugins dir.

func InstallFromURL

func InstallFromURL(ctx context.Context, url, destRoot string) error

InstallFromURL downloads an archive (zip or tar.gz) from url, extracts it, verifies the manifest, and installs into destRoot. Used by the marketplace install action and `<app> plugin install <url|name>`.

func ResolveSource

func ResolveSource(ctx context.Context, src string) (string, func(), error)

ResolveSource turns a path / url / archive into a directory containing {binary, plugin.json}, returning the dir and a cleanup func. A bare existing directory is returned as-is.

func RunDir

func RunDir() string

RunDir is where wick pins plugin Unix sockets: <home>/.wick/run, overridable with WICK_PLUGIN_SOCKET_DIR. go-plugin creates the socket under here (0700) instead of the OS temp dir.

Types

type Available

type Available struct {
	Key         string `json:"key"`
	Name        string `json:"name"`
	Description string `json:"description"`
	Version     string `json:"version"`
	// Assets maps "<goos>/<goarch>" → direct download URL of the release zip.
	Assets map[string]string `json:"assets"`
}

Available is one installable connector surfaced by the catalog.

Key vs Name mirrors connector.Meta: Key is the slug (= source folder = zip name = install dir = registry key — the one identity used for matching and install); Name is the free display string shown in the UI. Older catalog entries that only have "name" are tolerated: parseCatalog backfills Key from Name when Key is absent.

func (Available) AssetFor

func (a Available) AssetFor(host string) string

AssetFor returns the download URL matching host (e.g. "linux/arm64"), or "".

type Catalog

type Catalog struct {
	// contains filtered or unexported fields
}

Catalog discovers connector plugins that are AVAILABLE to install from a single curated JSON file checked into the wick repo's default branch (raw.githubusercontent.com/yogasw/wick/master/plugins/plugins.json).

This is deliberately NOT the GitHub Releases API: listing is a plain raw-file fetch, so it never hits the API rate limit and needs no token. The JSON carries, per plugin, the direct release download URL for each os/arch — the binary is pulled from the GitHub Release only when the user clicks Download.

func DefaultRegistry

func DefaultRegistry() *Catalog

DefaultRegistry builds a Catalog from env overrides:

WICK_PLUGIN_CATALOG  full URL to plugins.json (default: wick repo master, plugins/plugins.json)

Named DefaultRegistry for call-site compatibility with the earlier API.

func (*Catalog) List

func (c *Catalog) List(ctx context.Context) ([]Available, error)

List returns every installable connector from the catalog JSON. Served from cache while the TTL is fresh; otherwise a conditional GET (ETag) reuses the cache on a 304.

func (*Catalog) Resolve

func (c *Catalog) Resolve(ctx context.Context, name, host string) (Available, string, error)

Resolve returns the arch-matching download URL for the named connector. host defaults to the current runtime when empty.

type ConnGetter

type ConnGetter func(key string) (*Lease, error)

ConnGetter returns a lease on a live plugin connection for a connector key. The manager's Client method satisfies it; tests pass a fake.

type Found

type Found struct {
	Key        string
	BinaryPath string
	Manifest   wickplugin.Manifest
}

Found is one discovered plugin: its key, on-disk binary, and parsed manifest envelope.

func Scan

func Scan(dir string) ([]Found, error)

Scan walks dir/<name>/plugin.json and returns one Found per connector. Each plugin.json is a manifest envelope; the binary is resolved from the manifest's Entry (falling back to the directory name). A missing dir is not an error (returns nil) — plugins are optional.

type Lease

type Lease struct {
	Conn wickplugin.GRPCConn
	// contains filtered or unexported fields
}

Lease is a borrowed plugin connection. Release MUST be called exactly once (the adapter does so via defer) so the Manager can account for in-flight calls and free the subprocess for eviction.

func (*Lease) Release

func (l *Lease) Release()

Release returns the lease to the Manager. Safe to call on a nil-release lease.

type Manager

type Manager struct {
	// contains filtered or unexported fields
}

Manager owns connector plugin subprocesses keyed by connector Meta.Key.

func Load

func Load(dir string, idleTimeout time.Duration, enabled func(string) bool) (*Manager, int, error)

Load scans dir, builds a Manager over the discovered binaries, registers each plugin module via connectors.Register (replace-by-key so a plugin overrides the compiled-in builtin of the same key), and returns the Manager (caller owns its KillAll on shutdown). Returns a nil Manager when no plugins are present. When enabled is non-nil, keys for which it returns false are excluded from the Manager (not spawnable) and not registered; a nil enabled treats all discovered plugins as enabled.

func NewManager

func NewManager(binaries map[string]string, idleTimeout time.Duration) *Manager

NewManager builds a Manager and starts the idle sweeper.

func (*Manager) Client

func (m *Manager) Client(key string) (*Lease, error)

Client returns a lease on a live gRPC connection for key, spawning the subprocess on first use (lazy) and re-spawning if a previous process died. Release the lease when the call completes.

func (*Manager) IsPlugin

func (m *Manager) IsPlugin(key string) bool

IsPlugin reports whether key is served by a plugin subprocess.

func (*Manager) KillAll

func (m *Manager) KillAll()

KillAll reaps every subprocess (call on app shutdown). It is safe to call more than once: the stop channel is closed at most once.

func (*Manager) RemoveBinary

func (m *Manager) RemoveBinary(key string)

RemoveBinary drops a connector key and kills its running subprocess (if any).

func (*Manager) ResolveIdentity

func (m *Manager) ResolveIdentity(ctx context.Context, key, token string) (string, string, error)

ResolveIdentity spawns-if-needed and asks the plugin to resolve an OAuth token's owner.

func (*Manager) SetBinary

func (m *Manager) SetBinary(key, path string)

SetBinary registers or updates the on-disk binary path for a connector key. If a subprocess is already running for this key it is killed so the next Client call spawns the new binary.

func (*Manager) WarmUp

func (m *Manager) WarmUp()

WarmUp eagerly spawns every warm connector that has a registered binary so it is hot before the first call. Failures are logged and skipped — boot must not abort. Call once at boot after Load wires the binaries.

type Reloader

type Reloader struct {
	// contains filtered or unexported fields
}

Reloader watches the plugins dir and reconciles installed plugins into the running service without a restart. It polls on a fixed interval (no fsnotify dependency).

func NewReloader

func NewReloader(dir string, svc moduleSink, mgr *Manager, interval time.Duration, store enabledChecker) *Reloader

NewReloader builds a Reloader. interval <= 0 defaults to 5s.

func (*Reloader) Reload

func (r *Reloader) Reload(ctx context.Context)

Reload triggers an immediate reconcile (for in-process callers, e.g. CLI install).

func (*Reloader) Start

func (r *Reloader) Start(ctx context.Context)

Start runs the poll loop until Stop is called or ctx is cancelled. Call once.

func (*Reloader) Stop

func (r *Reloader) Stop()

Stop ends the poll loop.

type StateStore

type StateStore struct {
	// contains filtered or unexported fields
}

StateStore reads and writes the plugin enable/disable overlay.

func NewStateStore

func NewStateStore(db *gorm.DB) *StateStore

NewStateStore wraps db. A nil db yields a store whose Enabled defaults to true.

func (*StateStore) Enabled

func (s *StateStore) Enabled(key string) bool

Enabled reports whether key may be registered/spawned. Missing row or any error -> true (default-on; never hide a plugin because of a read error).

func (*StateStore) List

func (s *StateStore) List() (map[string]bool, error)

List returns key -> enabled for all overlay rows.

func (*StateStore) SetEnabled

func (s *StateStore) SetEnabled(key string, enabled bool) error

SetEnabled upserts the overlay row for key. A map is used so gorm writes the literal enabled value; a struct would let the `default:true` tag override a zero-value false on insert.

Jump to

Keyboard shortcuts

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