sdk

package
v1.0.0-beta.7 Latest Latest
Warning

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

Go to latest
Published: Jul 14, 2026 License: ISC Imports: 13 Imported by: 0

Documentation

Overview

Package sdk provides the plugin author interface and helpers for implementing preflight plugins as standalone executables speaking JSON-RPC over stdin/stdout.

Index

Constants

View Source
const ProtocolVersion = "1"

ProtocolVersion is the wire-protocol version this host and SDK speak. Plugins must echo it back in their initialize response; a mismatch or absence is a plugin_protocol error (pre-v1 plugins are rejected).

Variables

This section is empty.

Functions

func IsProtocolError

func IsProtocolError(err error) bool

IsProtocolError reports whether err is a *ProtocolError.

func Serve

func Serve(m Module)

Serve runs the JSON-RPC loop for the given module, reading requests from stdin and writing responses to stdout. Call this from your plugin's main().

The host delivers TargetInfo at initialize; the Handle given to Check/Apply exposes it (plus RunCommand/PutFile/GetFile/Output) by calling back over the same stdio channel — both sides act as JSON-RPC client and server.

Types

type ApplyResult

type ApplyResult struct {
	Message string `json:"message,omitempty"`
	Error   string `json:"error,omitempty"`
}

ApplyResult is returned by a module's Apply method.

type CheckResult

type CheckResult struct {
	NeedsChange bool   `json:"needs_change"`
	Message     string `json:"message,omitempty"`
	Error       string `json:"error,omitempty"`
}

CheckResult is returned by a module's Check method.

type Client

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

Client is the runner-side handle for a JSON-RPC plugin peer. It is transport-agnostic: it speaks the wire protocol over the reader/writer pair supplied at construction and delegates cleanup to an optional close function.

Both sides act as client and server: the Client sends initialize/check/apply requests and forwards the plugin's output notifications to an OutputFunc, while simultaneously answering the plugin's handle-op requests (run_command/put_file/get_file) through the bound HandleServer. One outgoing call is in flight at a time (the codec serializes them); incoming handle-op requests are handled concurrently so a plugin's Check can issue a RunCommand while the host's check call is still outstanding.

func NewClientContext

func NewClientContext(ctx context.Context, executablePath string, info TargetInfo, ops HandleServer) (*Client, error)

NewClientContext starts the plugin, performs the v1 initialize handshake (protocol_version + enriched TargetInfo), and binds the plugin's handle ops to ops. Used by the runtime adapter against a real target.

func NewClientForInspection

func NewClientForInspection(ctx context.Context, executablePath string) (*Client, error)

NewClientForInspection starts a plugin with no target bound, for plugin list/info/staging paths that only read name/version.

func NewClientFromCmd

func NewClientFromCmd(cmd *exec.Cmd, info TargetInfo, ops HandleServer) (*Client, error)

NewClientFromCmd starts the given command and connects a Client to its stdin/stdout for JSON-RPC communication. The command must not have been started yet. info is delivered at initialize; ops answers handle-op requests.

func NewClientStream

func NewClientStream(r io.Reader, w io.Writer, closeFn func() error, info TargetInfo, ops HandleServer) (*Client, error)

NewClientStream connects a Client to a JSON-RPC plugin peer over the given reader/writer pair, performs the initialize handshake (sending protocol_version and the enriched TargetInfo, and requiring the plugin to echo protocol_version back), and binds the plugin's handle-op requests to ops. If closeFn is non-nil it is invoked exactly once from Close (and on initialize failure).

ops carries the target effects the plugin exercises (RunCommand/PutFile/GetFile). For inspection paths with no target, pass NoopHandleServer(). info is the TargetInfo delivered to the plugin at initialize.

func (*Client) Apply

func (c *Client) Apply(ctx context.Context, args map[string]any, out OutputFunc) (ApplyResult, error)

Apply calls the plugin's apply method, forwarding output to out.

func (*Client) Check

func (c *Client) Check(ctx context.Context, args map[string]any, out OutputFunc) (CheckResult, error)

Check calls the plugin's check method, forwarding output to out.

func (*Client) Close

func (c *Client) Close() error

Close terminates the plugin peer (hard kill on the transport, as before). Subsequent calls are no-ops.

func (*Client) Name

func (c *Client) Name() string

Name returns the plugin's self-reported name.

func (*Client) Version

func (c *Client) Version() string

Version returns the plugin's self-reported version.

type CommandResult

type CommandResult struct {
	Stdout   string `json:"stdout"`
	Stderr   string `json:"stderr"`
	ExitCode int    `json:"exit_code"`
}

CommandResult is the outcome of a RunCommand handle op: the script runs in the target's native shell and returns separated stdout/stderr and the exit code.

type DiscoveredPlugin

type DiscoveredPlugin struct {
	Name   string
	Path   string
	Source string
}

DiscoveredPlugin is one plugin executable found during scanning.

func Scan

func Scan(opts DiscoveryOptions) ([]DiscoveredPlugin, error)

Scan returns every matching plugin executable in scan order. PreferredDirs are searched before the default binary/home/cwd directories.

type DiscoveryOptions

type DiscoveryOptions struct {
	BinaryDir           string
	WorkingDir          string
	PreferredDirs       []string
	DisableFallbackDirs bool
}

DiscoveryOptions controls plugin scan order.

type Handle

type Handle interface {
	HandleServer
	// Info returns the TargetInfo delivered at initialize.
	Info() TargetInfo
	// Output emits a streaming line back to the host's output channel.
	Output(line string)
}

Handle is given to a plugin's Check/Apply. ALL target effects flow through it — including against the local target — so plugins are brought in line with first-party modules. The three target primitives are RunCommand, PutFile/GetFile, and TargetInfo (delivered at initialize and cached). Output carries streaming lines back to the host.

One target op is in flight per session: a plugin that calls RunCommand must wait for its result before issuing another op (or a PutFile/GetFile). For high-latency transports, batch work into a single script-shaped RunCommand rather than many round trips.

File transfer is whole-file in v1: PutFile/GetFile carry the entire payload as a single base64-encoded JSON-RPC frame buffered in memory. Chunked streaming for large files is deferred to v2; keep payloads small (a few MB at most).

type HandleServer

type HandleServer interface {
	// RunCommand executes script in the target's native shell (POSIX sh or
	// PowerShell per TargetInfo.RuntimeKind) and returns stdout, stderr, and
	// the exit code. This is the batching lever for high-latency transports:
	// prefer one script that does several things over several ops.
	RunCommand(ctx context.Context, script string) (CommandResult, error)
	// PutFile writes data to path on the target. File transfer is whole-file
	// in v1: the payload is base64-encoded into a single JSON-RPC frame
	// buffered in memory; chunked streaming is deferred to v2.
	PutFile(ctx context.Context, path string, data []byte) error
	// GetFile reads the contents of path from the target (whole-file, v1).
	GetFile(ctx context.Context, path string) ([]byte, error)
}

HandleServer is the host-side backend a Client binds to. Every transport (Local, SSH-POSIX, SSH-Windows, WinRM) implements it; the Client dispatches plugin handle-op requests to it. Handle embeds this so the subset relationship between handle ops and the full plugin Handle stays explicit.

func NoopHandleServer

func NoopHandleServer() HandleServer

NoopHandleServer returns a HandleServer whose methods report that no target is bound. It is intended for plugin inspection (plugin list/info/staging) where there is no target to operate against.

type InitializeParams

type InitializeParams struct {
	ProtocolVersion string     `json:"protocol_version"`
	Target          TargetInfo `json:"target"`
}

InitializeParams is sent by the host in the initialize request.

type InitializeResult

type InitializeResult struct {
	Name            string `json:"name"`
	Version         string `json:"version"`
	ProtocolVersion string `json:"protocol_version"`
}

InitializeResult is the plugin's initialize response. ProtocolVersion must equal ProtocolVersion or the host rejects the plugin with a plugin_protocol error.

type Module

type Module interface {
	// Name returns the module's canonical name (e.g. "my-module").
	Name() string
	// Version returns the module's semantic version.
	Version() string
	// Check reports whether the system is already in the desired state.
	// NeedsChange must be true if the system is NOT yet in the desired state
	// (i.e., Apply should be called). Target effects go through h.
	Check(args map[string]any, h Handle) (CheckResult, error)
	// Apply brings the system into the desired state, using h for all target
	// effects.
	Apply(args map[string]any, h Handle) (ApplyResult, error)
}

Module is the interface plugin authors implement. Check and Apply receive a Handle: ALL target effects flow through it, including against the local target. This brings plugins in line with first-party modules.

One target op is in flight per session. For high-latency transports, batch work into a single script-shaped RunCommand instead of many round trips.

type OutputFunc

type OutputFunc func(line string)

OutputFunc is called for each line of streaming output emitted during Check or Apply. On the host side it forwards plugin output notifications to the runner; on the plugin side it is exposed through Handle.Output.

type PluginStatus

type PluginStatus struct {
	Name         string
	Path         string
	Source       string
	Version      string
	Initialized  bool
	ErrorMessage string
}

PluginStatus describes a discovered plugin plus initialization status.

func Inspect

func Inspect(opts DiscoveryOptions) ([]PluginStatus, error)

Inspect initializes each discovered plugin and returns its reported version or the initialization failure.

func InspectPlugin

func InspectPlugin(path, source string) PluginStatus

InspectPlugin initializes a single plugin executable and returns its status.

type ProtocolError

type ProtocolError struct {
	Got  string
	Want string
}

ProtocolError reports a plugin that failed the protocol-version handshake. Pre-v1 plugins (no protocol_version) and protocol mismatches both surface as this typed error so the host can raise the distinct plugin_protocol class.

func (*ProtocolError) Error

func (e *ProtocolError) Error() string

type TargetInfo

type TargetInfo struct {
	Family         string `json:"family"`
	Name           string `json:"name"`
	Version        string `json:"version"`
	Arch           string `json:"arch"`
	Hostname       string `json:"hostname"`
	PackageManager string `json:"package_manager"`
	Init           string `json:"init"`
	RuntimeKind    string `json:"runtime_kind"`
}

TargetInfo is the enriched target context delivered to a plugin at initialize. Absent signals are empty strings, never missing keys, so plugin code can branch on them with simple equality. RuntimeKind tells the plugin which shell RunCommand speaks (posix-sh or windows-powershell).

Jump to

Keyboard shortcuts

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