Documentation
¶
Overview ¶
Package plugin provides the public contract for writing Restish out-of-process plugins.
Restish supports three plugin styles:
- hook plugins for auth, middleware, loader, and formatter hooks
- command plugins for top-level workflow commands
- TLS signer plugins for external mTLS signing
Startup contract ¶
Plugin binaries are discovered as executables named "restish-<name>". Restish starts them with one of these startup flags:
- StartupFlagManifest: write a CBOR Manifest to stdout
- StartupFlagCommands: write a CBOR command list to stdout (command plugins)
Most plugins should use HandleStartupFlags or Run instead of manually handling startup flags.
Runtime transport ¶
All messages — startup responses and runtime messages alike — are plain CBOR data items written directly to stdin/stdout. CBOR is self-delimiting, so no length prefix or other framing is needed. Any language with a CBOR library can implement a plugin.
Use WriteMessage and ReadMessage for runtime messages. For command plugins, prefer Run and CommandClient unless you need lower-level stream control. WriteManifest and WriteCommands are convenience wrappers for startup responses.
For hook plugins that read a single message, ReadMessage(r, v) is sufficient. Command and TLS-signer plugins receive multiple messages on the same stdin, so they must create one Decoder with NewDecoder(os.Stdin) and call ReadMessage on it for every read. Discarding the Decoder between calls loses bytes that were already buffered internally.
Command-plugin messages ¶
Command plugins should prefer the typed message structs in messages.go:
- InitMsg for the initial host -> plugin command selection
- HTTPRequestMsg / HTTPResponseMsg for delegated HTTP
- APISpecMsg / APISpecResponseMsg for fetching registered API specs
- ListAPIsMsg / ListProfilesMsg for config discovery
- ConfigReadMsg for effective API/profile/plugin config
- PromptMsg / ConfirmMsg for user interaction
- ResponseMsg, StdoutDataMsg, StderrDataMsg, WarnMsg, and DoneMsg for output
For simple command plugins, plugin.Run and CommandClient are usually enough.
Hook-plugin payload shapes ¶
Most hook plugins receive one CBOR map and, except for formatter hooks, return one CBOR reply map.
- auth: request contains api_name, profile_name, params, and request metadata reply typically returns request.header updates
- request-middleware: request contains the outbound request metadata reply can return updated request headers
- response-middleware: request contains original request metadata plus normalized response fields reply may set drop, follow, or response
- loader: request contains content_type and raw body bytes reply returns an OpenAPI document in body plus optional content_type
- formatter: plugins receive a stream of formatter messages (`start`, `item`, `end`) on stdin and write final formatted bytes directly to stdout. For ordinary full-response renders the host usually sends `start` with the full normalized response body followed by `end`. For paginated or event-stream output the host sends `start`, then one or more `item` messages, then `end`.
See site/content/en/docs/plugins/quickstart.md for the fastest practical path to a working plugin, and docs/design/019-hook-plugins.md plus docs/design/020-command-plugins.md for the full protocol details.
Index ¶
- Constants
- Variables
- func ArgsWithoutStartupFlags(args []string) []string
- func HandleStartupFlags(w io.Writer, m Manifest, cmds []CommandDecl) bool
- func MessageType(raw []byte) string
- func MsgBytes(v any) []byte
- func MsgInt(v any) int
- func MsgStrings(v any) []string
- func ReadMessage(r io.Reader, v any) error
- func Run(m Manifest, cmds []CommandDecl, ...)
- func WriteCommands(w io.Writer, cmds []CommandDecl) error
- func WriteManifest(w io.Writer, m Manifest) error
- func WriteMessage(w io.Writer, v any) error
- type APIOperation
- type APIParam
- type APISpecMsg
- type APISpecResponseMsg
- type AuthHookInput
- type AuthHookOutput
- type CommandClient
- func (c *CommandClient) ConfigRead(api, profile, pluginName string) (*ConfigReadResponseMsg, error)
- func (c *CommandClient) ConfigReadContext(ctx context.Context, api, profile, pluginName string) (*ConfigReadResponseMsg, error)
- func (c *CommandClient) Confirm(message string) (*ConfirmResponseMsg, error)
- func (c *CommandClient) ConfirmContext(ctx context.Context, message string) (*ConfirmResponseMsg, error)
- func (c *CommandClient) Do(req *HTTPRequestMsg) (*HTTPResponseMsg, error)
- func (c *CommandClient) Done(exitCode int) error
- func (c *CommandClient) FetchAPISpec(name string) (*APISpecResponseMsg, error)
- func (c *CommandClient) FetchAPISpecContext(ctx context.Context, name, profile string) (*APISpecResponseMsg, error)
- func (c *CommandClient) ListAPIs() (*ListAPIsResponseMsg, error)
- func (c *CommandClient) ListAPIsContext(ctx context.Context) (*ListAPIsResponseMsg, error)
- func (c *CommandClient) ListProfiles(api string) (*ListProfilesResponseMsg, error)
- func (c *CommandClient) ListProfilesContext(ctx context.Context, api string) (*ListProfilesResponseMsg, error)
- func (c *CommandClient) Progress(text string) error
- func (c *CommandClient) Prompt(message string, hidden bool) (*PromptResponseMsg, error)
- func (c *CommandClient) PromptContext(ctx context.Context, message string, hidden bool) (*PromptResponseMsg, error)
- func (c *CommandClient) ReadMessage(v any) error
- func (c *CommandClient) Response(status int, headers map[string][]string, body any) error
- func (c *CommandClient) StderrWriter() io.Writer
- func (c *CommandClient) StdoutWriter() io.Writer
- func (c *CommandClient) Warn(text string) error
- func (c *CommandClient) WriteMessage(v any) error
- func (c *CommandClient) WriteStderr(data []byte) error
- func (c *CommandClient) WriteStdout(data []byte) error
- type CommandDecl
- type CommandDiscoveryResponse
- type ConfigReadMsg
- type ConfigReadResponseMsg
- type ConfirmMsg
- type ConfirmResponseMsg
- type Decoder
- type DoneMsg
- type FollowRequest
- type FormatterRequest
- type FormatterResponse
- type HTTPRequestMsg
- type HTTPResponseMsg
- type HookRequest
- type HookRequestHeaderUpdate
- type HookResponse
- type HookResponseUpdate
- type InitMsg
- type ListAPIsMsg
- type ListAPIsResponseMsg
- type ListProfilesMsg
- type ListProfilesResponseMsg
- type LoaderRequest
- type LoaderResponse
- type LogMsg
- type Manifest
- type ProgressMsg
- type PromptMsg
- type PromptResponseMsg
- type RequestMiddlewareInput
- type RequestMiddlewareOutput
- type ResponseMiddlewareInput
- type ResponseMiddlewareOutput
- type ResponseMsg
- type SpinnerMsg
- type StderrDataMsg
- type StdinCloseMsg
- type StdinDataMsg
- type StdoutDataMsg
- type TLSSignerInitMsg
- type TLSSignerReadyMsg
- type TLSSignerShutdownMsg
- type TLSSignerSignMsg
- type TLSSignerSignedMsg
- type TerminalContext
- type WarnMsg
Constants ¶
const ( // CommandPluginProtocolVersion is the current command-plugin discovery // protocol version. A plugin that emits a larger version requires a newer // Restish host. CommandPluginProtocolVersion = 1 // StartupFlagManifest asks a plugin to write its Manifest and exit. StartupFlagManifest = "--rsh-plugin-manifest" // StartupFlagCommands asks a command plugin to write its command list and exit. StartupFlagCommands = "--rsh-plugin-commands" // StartupFlagColor tells a command plugin whether host terminal color is enabled. StartupFlagColor = "--rsh-color" // StartupFlagStdoutTTY tells a command plugin whether host stdout is a TTY. StartupFlagStdoutTTY = "--rsh-stdout-tty" // StartupFlagStderrTTY tells a command plugin whether host stderr is a TTY. StartupFlagStderrTTY = "--rsh-stderr-tty" // StartupFlagTheme carries the host's configured terminal theme as JSON. StartupFlagTheme = "--rsh-theme" )
const ( // FeatureManifestRequiredFeatures means the host understands manifest // required_features validation. FeatureManifestRequiredFeatures = "manifest.required_features" // FeatureLoaderSourceMetadata means loader hooks receive content_type, // source_url, and local_path metadata when available. FeatureLoaderSourceMetadata = "loader.source_metadata" // FeatureRequestFinalBody means auth and request-middleware hooks may // receive the final request body bytes when Restish has them. FeatureRequestFinalBody = "request.final_body" )
const ( // Plugin → host requests. MsgTypeInit = "init" MsgTypeHTTPRequest = "http-request" MsgTypeAPISpec = "api-spec" MsgTypeListAPIs = "list-apis" MsgTypeListProfiles = "list-profiles" MsgTypeConfigRead = "config-read" MsgTypePrompt = "prompt" MsgTypeConfirm = "confirm" MsgTypeResponse = "response" MsgTypeDone = "done" MsgTypeStdoutData = "stdout-data" MsgTypeStderrData = "stderr-data" MsgTypeWarn = "warn" MsgTypeProgress = "progress" MsgTypeSpinner = "spinner" MsgTypeLog = "log" // Host → plugin responses. MsgTypeHTTPResponse = "http-response" MsgTypeAPISpecResponse = "api-spec-response" MsgTypeListAPIsResponse = "list-apis-response" MsgTypeListProfilesResponse = "list-profiles-response" MsgTypeConfigReadResponse = "config-read-response" MsgTypePromptResponse = "prompt-response" MsgTypeConfirmResponse = "confirm-response" // Host → plugin passthrough-stdio data. MsgTypeStdinData = "stdin-data" MsgTypeStdinClose = "stdin-close" // TLS signer plugin protocol. MsgTypeTLSSignerSign = "sign" // host → plugin: sign request MsgTypeTLSSignerReady = "ready" // plugin → host: ready with certificate MsgTypeTLSSignerShutdown = "shutdown" // host → plugin: graceful shutdown request )
Message type constants for the command plugin protocol. Use these instead of bare strings to avoid typos; a mismatched type string causes the host or plugin to silently ignore the message.
Variables ¶
var DecMode = func() cbor.DecMode { dm, err := cbor.DecOptions{ DefaultMapType: reflect.TypeOf(map[string]any{}), MaxNestedLevels: maxCBORNestedLevels, MaxArrayElements: maxCBORArrayElements, MaxMapPairs: maxCBORMapPairs, }.DecMode() if err != nil { panic("plugin: creating CBOR decode mode: " + err.Error()) } return dm }()
DecMode is a CBOR decode mode configured to use map[string]any for all CBOR maps (including nested ones), rather than the default map[any]any. It also applies the same structural limits that Restish uses for plugin messages, so buggy plugins cannot force unbounded map, array, or nesting allocations before typed validation runs.
Functions ¶
func ArgsWithoutStartupFlags ¶
ArgsWithoutStartupFlags strips Restish-injected startup and terminal flags from the beginning of args. Flags with the same names after the first user argument are preserved as user input.
func HandleStartupFlags ¶
func HandleStartupFlags(w io.Writer, m Manifest, cmds []CommandDecl) bool
HandleStartupFlags checks the Restish-injected startup prefix in os.Args for StartupFlagManifest or StartupFlagCommands. If either flag is found the appropriate CBOR response is written to w and the function returns true — the caller should return from main immediately.
func main() {
if plugin.HandleStartupFlags(os.Stdout, manifest, cmds) { return }
// ... command dispatch
}
func MessageType ¶
MessageType returns the "type" field of a raw CBOR message without fully decoding it. Returns an empty string if the field is absent or the data is not valid CBOR.
func MsgBytes ¶
MsgBytes coerces a CBOR-decoded value into a []byte. CBOR byte strings decode to []byte natively. Some CBOR implementations or intermediate representations may produce a string or a []any of integers instead; this handles all three forms so callers do not need to special-case.
func MsgInt ¶
MsgInt coerces a CBOR-decoded value into an int. CBOR integers may be decoded as int, int64, uint64, or float64 depending on the decoder configuration and value; this handles all common forms.
func MsgStrings ¶
MsgStrings coerces a CBOR-decoded value into a []string. CBOR arrays of strings decode to []any; this extracts the string items.
func ReadMessage ¶
ReadMessage reads one CBOR data item from r and unmarshals it into v (which must be a pointer). It is intended for one-shot reads such as hook plugins. For command or TLS-signer plugins that receive multiple messages, create a Decoder with NewDecoder and call ReadMessage on that instead.
func Run ¶
func Run(m Manifest, cmds []CommandDecl, fn func(command string, args []string, client *CommandClient) error)
Run is the complete main-loop for simple command plugins. It handles startup flags, reads the init message from os.Stdin, creates a CommandClient, and calls fn with the command name, args, and client.
On success Run sends a done message with exit code 0 and returns. On error Run sends the error to stderr and exits with code 1.
func main() {
plugin.Run(manifest, cmds, func(command string, args []string, c *plugin.CommandClient) error {
// ... handle command
return nil
})
}
func WriteCommands ¶
func WriteCommands(w io.Writer, cmds []CommandDecl) error
WriteCommands serialises cmds as a CBOR map with a "commands" array and writes it to w. It is the canonical way to respond to --rsh-plugin-commands.
case plugin.StartupFlagCommands:
return plugin.WriteCommands(os.Stdout, cmds)
func WriteManifest ¶
WriteManifest serialises m as a CBOR data item and writes it to w. It is the canonical way to respond to --rsh-plugin-manifest.
case plugin.StartupFlagManifest:
return plugin.WriteManifest(os.Stdout, m)
Types ¶
type APIOperation ¶
type APIOperation struct {
ID string `cbor:"id"`
Method string `cbor:"method"`
Path string `cbor:"path"`
Summary string `cbor:"summary,omitempty"`
Description string `cbor:"description,omitempty"`
Deprecated bool `cbor:"deprecated,omitempty"`
Parameters []APIParam `cbor:"parameters,omitempty"`
HasBody bool `cbor:"has_body,omitempty"`
BodyRequired bool `cbor:"body_required,omitempty"`
RequestMediaType string `cbor:"request_media_type,omitempty"`
RequestSchema map[string]any `cbor:"request_schema,omitempty"`
RequestSchemaDialect string `cbor:"request_schema_dialect,omitempty"`
MCPIgnore bool `cbor:"mcp_ignore,omitempty"`
}
APIOperation is the host's resolved, config-aware representation of one OpenAPI HTTP operation. It mirrors the generated-command model so command plugins do not need to re-parse raw OpenAPI specs.
type APIParam ¶
type APIParam struct {
Name string `cbor:"name"`
In string `cbor:"in"`
Required bool `cbor:"required,omitempty"`
Description string `cbor:"description,omitempty"`
Type string `cbor:"type,omitempty"`
ItemType string `cbor:"item_type,omitempty"`
Style string `cbor:"style,omitempty"`
Explode *bool `cbor:"explode,omitempty"`
AllowReserved bool `cbor:"allow_reserved,omitempty"`
ContentMediaType string `cbor:"content_media_type,omitempty"`
Schema map[string]any `cbor:"schema,omitempty"`
SchemaDialect string `cbor:"schema_dialect,omitempty"`
Enum []string `cbor:"enum,omitempty"`
}
APIParam is a resolved operation parameter for APIOperation.
type APISpecMsg ¶
type APISpecMsg struct {
Type string `cbor:"type"`
RequestID string `cbor:"request_id,omitempty"`
Name string `cbor:"name"`
Profile string `cbor:"profile,omitempty"`
}
APISpecMsg asks the host to load the OpenAPI spec for a registered API.
type APISpecResponseMsg ¶
type APISpecResponseMsg struct {
Type string `cbor:"type"`
RequestID string `cbor:"request_id,omitempty"`
Name string `cbor:"name"`
Profile string `cbor:"profile,omitempty"`
ContentType string `cbor:"content_type,omitempty"`
Raw []byte `cbor:"raw,omitempty"`
Operations []APIOperation `cbor:"operations,omitempty"`
Error string `cbor:"error,omitempty"`
}
APISpecResponseMsg is the host reply to an APISpecMsg.
type AuthHookInput ¶
type AuthHookInput struct {
Type string `cbor:"type" json:"type"`
API string `cbor:"api" json:"api"`
Profile string `cbor:"profile" json:"profile"`
Params map[string]string `cbor:"params" json:"params"`
Request HookRequest `cbor:"request" json:"request"`
}
AuthHookInput is sent to plugins registered for the "auth" hook.
type AuthHookOutput ¶
type AuthHookOutput struct {
Request *HookRequestHeaderUpdate `cbor:"request,omitempty" json:"request,omitempty"`
}
AuthHookOutput is the reply from an "auth" hook plugin.
type CommandClient ¶
type CommandClient struct {
// StdinDataHandler is called with each chunk of stdin data received from
// the host while Do() is waiting for an http-response. Set this before
// calling Do() when passthrough_stdio is active.
StdinDataHandler func(data []byte)
// StdinCloseHandler is called when the host signals that stdin has reached
// EOF while Do() is waiting for an http-response.
StdinCloseHandler func()
// contains filtered or unexported fields
}
CommandClient is the plugin-side counterpart of the host command-plugin runner. It wraps the host stdin/stdout pair and provides helpers for making authenticated HTTP calls, writing output, and signalling completion.
Most command plugins should create one with NewCommandClient and use it for all communication with the host.
Plugins that declare passthrough_stdio should register StdinDataHandler and StdinCloseHandler before calling Do(). These handlers are invoked for any stdin-data or stdin-close frames that arrive after Do starts the background response reader.
func NewCommandClient ¶
func NewCommandClient(in io.Reader, out io.Writer) *CommandClient
NewCommandClient returns a CommandClient backed by the given reader/writer. Pass os.Stdin and os.Stdout from a plugin's main function. A single Decoder is created from in and reused for all reads. Call ReadMessage for startup messages before calling Do; after Do starts the background response reader, all host replies are routed through Do.
func NewCommandClientFromDecoder ¶
func NewCommandClientFromDecoder(dec *Decoder, out io.Writer) *CommandClient
NewCommandClientFromDecoder returns a CommandClient that continues reading from an existing decoder. Use this when startup code has already consumed initial messages from the same input stream.
func (*CommandClient) ConfigRead ¶
func (c *CommandClient) ConfigRead(api, profile, pluginName string) (*ConfigReadResponseMsg, error)
ConfigRead asks the host for API/profile config and/or plugin config.
func (*CommandClient) ConfigReadContext ¶
func (c *CommandClient) ConfigReadContext(ctx context.Context, api, profile, pluginName string) (*ConfigReadResponseMsg, error)
ConfigReadContext asks the host for API/profile config and/or plugin config, returning when ctx is canceled if the host does not reply.
func (*CommandClient) Confirm ¶
func (c *CommandClient) Confirm(message string) (*ConfirmResponseMsg, error)
Confirm asks the host to display message and read a yes/no value.
func (*CommandClient) ConfirmContext ¶
func (c *CommandClient) ConfirmContext(ctx context.Context, message string) (*ConfirmResponseMsg, error)
ConfirmContext asks the host to display message and read a yes/no value, returning when ctx is canceled if the host does not reply.
func (*CommandClient) Do ¶
func (c *CommandClient) Do(req *HTTPRequestMsg) (*HTTPResponseMsg, error)
Do sends one HTTP request to the host and blocks until the matching http-response arrives. While waiting, any stdin-data or stdin-close frames are dispatched to StdinDataHandler / StdinCloseHandler if registered.
Do is safe to call concurrently. Requests without RequestID are assigned one automatically so replies can be routed back to the caller.
func (*CommandClient) Done ¶
func (c *CommandClient) Done(exitCode int) error
Done signals that the plugin has finished. exitCode 0 means success; any other value causes the host to exit with that code.
func (*CommandClient) FetchAPISpec ¶
func (c *CommandClient) FetchAPISpec(name string) (*APISpecResponseMsg, error)
FetchAPISpec asks the host to load the OpenAPI spec or resolved operation metadata for a registered API name.
func (*CommandClient) FetchAPISpecContext ¶
func (c *CommandClient) FetchAPISpecContext(ctx context.Context, name, profile string) (*APISpecResponseMsg, error)
FetchAPISpecContext asks the host to load OpenAPI metadata for a registered API name and optional profile, returning when ctx is canceled if the host does not reply.
func (*CommandClient) ListAPIs ¶
func (c *CommandClient) ListAPIs() (*ListAPIsResponseMsg, error)
ListAPIs asks the host for configured API names.
func (*CommandClient) ListAPIsContext ¶
func (c *CommandClient) ListAPIsContext(ctx context.Context) (*ListAPIsResponseMsg, error)
ListAPIsContext asks the host for configured API names, returning when ctx is canceled if the host does not reply.
func (*CommandClient) ListProfiles ¶
func (c *CommandClient) ListProfiles(api string) (*ListProfilesResponseMsg, error)
ListProfiles asks the host for profile names under api.
func (*CommandClient) ListProfilesContext ¶
func (c *CommandClient) ListProfilesContext(ctx context.Context, api string) (*ListProfilesResponseMsg, error)
ListProfilesContext asks the host for profile names under api, returning when ctx is canceled if the host does not reply.
func (*CommandClient) Progress ¶
func (c *CommandClient) Progress(text string) error
Progress sends an informational progress message to the host.
func (*CommandClient) Prompt ¶
func (c *CommandClient) Prompt(message string, hidden bool) (*PromptResponseMsg, error)
Prompt asks the host to display message and read a value from the user.
func (*CommandClient) PromptContext ¶
func (c *CommandClient) PromptContext(ctx context.Context, message string, hidden bool) (*PromptResponseMsg, error)
PromptContext asks the host to display message and read a value from the user, returning when ctx is canceled if the host does not reply.
func (*CommandClient) ReadMessage ¶
func (c *CommandClient) ReadMessage(v any) error
ReadMessage reads one CBOR message from the host into v. Use this when the plugin needs to receive startup messages that are not covered by Do.
func (*CommandClient) Response ¶
Response asks the host to format and display response data using the same output machinery as regular Restish responses.
func (*CommandClient) StderrWriter ¶
func (c *CommandClient) StderrWriter() io.Writer
StderrWriter returns an io.Writer that routes writes through WriteStderr.
func (*CommandClient) StdoutWriter ¶
func (c *CommandClient) StdoutWriter() io.Writer
StdoutWriter returns an io.Writer that routes writes through WriteStdout.
func (*CommandClient) Warn ¶
func (c *CommandClient) Warn(text string) error
Warn sends a warning message to the host, which displays it to the user through whatever UI mechanism it uses.
func (*CommandClient) WriteMessage ¶
func (c *CommandClient) WriteMessage(v any) error
WriteMessage serialises v as a CBOR data item and sends it to the host. It is safe to call from multiple goroutines.
func (*CommandClient) WriteStderr ¶
func (c *CommandClient) WriteStderr(data []byte) error
WriteStderr writes data to the user's terminal stderr via the host.
func (*CommandClient) WriteStdout ¶
func (c *CommandClient) WriteStdout(data []byte) error
WriteStdout writes data to the user's terminal via the host.
type CommandDecl ¶
type CommandDecl struct {
// Name is the top-level command name contributed by the plugin.
Name string `cbor:"name" json:"name"`
// Short is the one-line help text shown in command listings.
Short string `cbor:"short,omitempty" json:"short,omitempty"`
// Long is optional extended help text.
Long string `cbor:"long,omitempty" json:"long,omitempty"`
// PassthroughStdio asks the host to forward stdin frames to the plugin.
PassthroughStdio bool `cbor:"passthrough_stdio,omitempty" json:"passthrough_stdio,omitempty"`
}
CommandDecl describes one command that a command-plugin exposes. It is used in the response to --rsh-plugin-commands.
type CommandDiscoveryResponse ¶
type CommandDiscoveryResponse struct {
ProtocolVersion int `cbor:"protocol_version,omitempty" json:"protocol_version,omitempty"`
Commands []CommandDecl `cbor:"commands" json:"commands"`
}
CommandDiscoveryResponse is the response to StartupFlagCommands.
type ConfigReadMsg ¶
type ConfigReadMsg struct {
Type string `cbor:"type"`
RequestID string `cbor:"request_id,omitempty"`
API string `cbor:"api,omitempty"`
Profile string `cbor:"profile,omitempty"`
// Plugin is the plugin's short name (without the "restish-" prefix).
// When set, the response includes PluginConfig populated from
// restish.json's plugins[Plugin] entry.
Plugin string `cbor:"plugin,omitempty"`
}
ConfigReadMsg asks the host for the effective configuration of an API profile (base URL, persistent headers and query params), and/or the plugin-specific config stored under plugins[Plugin] in restish.json.
type ConfigReadResponseMsg ¶
type ConfigReadResponseMsg struct {
Type string `cbor:"type"`
RequestID string `cbor:"request_id,omitempty"`
BaseURL string `cbor:"base_url,omitempty"`
Headers []string `cbor:"headers,omitempty"`
Query []string `cbor:"query,omitempty"`
Error string `cbor:"error,omitempty"`
// PluginConfig holds the parsed plugins[name] entry from restish.json,
// or nil when no config is stored for this plugin.
PluginConfig any `cbor:"plugin_config,omitempty"`
}
ConfigReadResponseMsg is the host reply to a ConfigReadMsg. Auth secrets are intentionally excluded.
type ConfirmMsg ¶
type ConfirmMsg struct {
Type string `cbor:"type"`
RequestID string `cbor:"request_id,omitempty"`
Message string `cbor:"message"`
}
ConfirmMsg asks the host to display a message and read a yes/no answer.
type ConfirmResponseMsg ¶
type ConfirmResponseMsg struct {
Type string `cbor:"type"`
RequestID string `cbor:"request_id,omitempty"`
Value bool `cbor:"value"`
Error string `cbor:"error,omitempty"`
}
ConfirmResponseMsg is the host reply to a ConfirmMsg.
type Decoder ¶
type Decoder struct {
// contains filtered or unexported fields
}
Decoder reads sequential CBOR messages from a stream. It maintains an internal read buffer, so a single Decoder instance must be reused for all reads from the same underlying reader — discarding it between calls would lose bytes that were already buffered.
Use NewDecoder for any reader from which multiple messages will be read (os.Stdin in a command or TLS-signer plugin). Hook plugins that receive exactly one message may use the package-level ReadMessage instead.
func NewDecoder ¶
NewDecoder returns a Decoder that reads from r.
Reuse a single Decoder for the lifetime of the stream. Constructing a new Decoder inside a read loop can discard bytes that were already buffered and silently lose messages.
func (*Decoder) ReadMessage ¶
ReadMessage reads one CBOR data item and unmarshals it into v (which must be a pointer).
type FollowRequest ¶
type FollowRequest struct {
Method string `cbor:"method,omitempty" json:"method,omitempty"`
URI string `cbor:"uri" json:"uri"`
Headers map[string]string `cbor:"headers,omitempty" json:"headers,omitempty"`
Body any `cbor:"body,omitempty" json:"body,omitempty"`
ContentType string `cbor:"content_type,omitempty" json:"content_type,omitempty"`
}
FollowRequest instructs the host to issue a follow-up HTTP request.
type FormatterRequest ¶
type FormatterRequest struct {
Type string `cbor:"type" json:"type"`
Format string `cbor:"format" json:"format"`
Color bool `cbor:"color,omitempty" json:"color,omitempty"`
Event string `cbor:"event" json:"event"`
Response FormatterResponse `cbor:"response" json:"response"`
}
FormatterRequest is sent to formatter plugins. Type is always "formatter" and Event is one of "start", "item", or "end".
type FormatterResponse ¶
type FormatterResponse struct {
Proto string `cbor:"proto,omitempty" json:"proto,omitempty"`
Status int `cbor:"status,omitempty" json:"status,omitempty"`
Headers map[string][]string `cbor:"headers,omitempty" json:"headers,omitempty"`
Links map[string]any `cbor:"links,omitempty" json:"links,omitempty"`
Body any `cbor:"body,omitempty" json:"body,omitempty"`
}
FormatterResponse is the normalized response shape forwarded to formatter plugins. The host may include the full response body on "start" for a normal one-shot render, or send body values incrementally on subsequent "item" messages for paginated and event-stream output.
type HTTPRequestMsg ¶
type HTTPRequestMsg struct {
Type string `cbor:"type"`
RequestID string `cbor:"request_id,omitempty"`
Method string `cbor:"method,omitempty"`
URI string `cbor:"uri"`
Headers map[string]string `cbor:"headers,omitempty"`
Body any `cbor:"body,omitempty"`
ContentType string `cbor:"content_type,omitempty"`
// NoCache bypasses the response cache for this request.
NoCache bool `cbor:"no_cache,omitempty"`
// CacheTTL (seconds) injects Cache-Control: max-age=N on the request.
// Only entries fresher than this TTL are served from the cache.
CacheTTL int `cbor:"cache_ttl,omitempty"`
// Timeout (seconds) sets a per-request deadline. 0 means no deadline.
Timeout int `cbor:"timeout,omitempty"`
// Filter is an optional shorthand or jq expression. When set, the host
// applies it to the full response document before sending it back.
Filter string `cbor:"filter,omitempty"`
}
HTTPRequestMsg asks the host to perform an HTTP request on behalf of the plugin and reply with an HTTPResponseMsg.
type HTTPResponseMsg ¶
type HTTPResponseMsg struct {
Type string `cbor:"type"`
RequestID string `cbor:"request_id,omitempty"`
Status int `cbor:"status"`
Headers map[string][]string `cbor:"headers,omitempty"`
URL string `cbor:"url,omitempty"`
Links map[string]any `cbor:"links,omitempty"`
Body any `cbor:"body"`
// Error is set when the HTTP request itself failed.
Error string `cbor:"error,omitempty"`
}
HTTPResponseMsg is the host reply to an HTTPRequestMsg.
type HookRequest ¶
type HookRequest struct {
Method string `cbor:"method" json:"method"`
URI string `cbor:"uri" json:"uri"`
Headers map[string][]string `cbor:"headers" json:"headers"`
Body []byte `cbor:"body,omitempty" json:"body,omitempty"`
BodySHA256 string `cbor:"body_sha256,omitempty" json:"body_sha256,omitempty"`
}
HookRequest carries the current HTTP request state forwarded to hook plugins.
type HookRequestHeaderUpdate ¶
type HookRequestHeaderUpdate struct {
// Each value is either a string or []string.
Headers map[string]any `cbor:"headers,omitempty" json:"headers,omitempty"`
}
HookRequestHeaderUpdate holds headers that a hook plugin wants to set or replace on the outgoing request. Only headers are applied; method and URI fields are intentionally absent because the request has already been prepared.
type HookResponse ¶
type HookResponse struct {
Status int `cbor:"status" json:"status"`
Headers map[string][]string `cbor:"headers" json:"headers"`
Body any `cbor:"body" json:"body"`
}
HookResponse carries the current HTTP response state forwarded to hook plugins.
type HookResponseUpdate ¶
type HookResponseUpdate struct {
Body any `cbor:"body,omitempty" json:"body,omitempty"`
// Each value is either a string or []string.
Headers map[string]any `cbor:"headers,omitempty" json:"headers,omitempty"`
}
HookResponseUpdate carries partial response modifications from a "response-middleware" plugin.
type InitMsg ¶
type InitMsg struct {
Type string `cbor:"type"`
Command string `cbor:"command"`
Args []string `cbor:"args"`
}
InitMsg is the first message sent from the host to the plugin after startup. It carries the sub-command name and the raw CLI arguments.
type ListAPIsMsg ¶
type ListAPIsMsg struct {
Type string `cbor:"type"`
RequestID string `cbor:"request_id,omitempty"`
}
ListAPIsMsg asks the host for the list of configured API names.
type ListAPIsResponseMsg ¶
type ListAPIsResponseMsg struct {
Type string `cbor:"type"`
RequestID string `cbor:"request_id,omitempty"`
APIs []string `cbor:"apis"`
Error string `cbor:"error,omitempty"`
}
ListAPIsResponseMsg is the host reply to a ListAPIsMsg.
type ListProfilesMsg ¶
type ListProfilesMsg struct {
Type string `cbor:"type"`
RequestID string `cbor:"request_id,omitempty"`
API string `cbor:"api"`
}
ListProfilesMsg asks the host for the profile names of a specific API.
type ListProfilesResponseMsg ¶
type ListProfilesResponseMsg struct {
Type string `cbor:"type"`
RequestID string `cbor:"request_id,omitempty"`
API string `cbor:"api"`
Profiles []string `cbor:"profiles"`
Error string `cbor:"error,omitempty"`
}
ListProfilesResponseMsg is the host reply to a ListProfilesMsg.
type LoaderRequest ¶
type LoaderRequest struct {
Type string `cbor:"type" json:"type"`
Body []byte `cbor:"body" json:"body"`
ContentType string `cbor:"content_type,omitempty" json:"content_type,omitempty"`
SourceURL string `cbor:"source_url,omitempty" json:"source_url,omitempty"`
LocalPath string `cbor:"local_path,omitempty" json:"local_path,omitempty"`
}
LoaderRequest is sent to plugins registered for the "loader" hook. Body contains the source document bytes. ContentType, SourceURL, and LocalPath are metadata from discovery or cache when the host has it.
type LoaderResponse ¶
type LoaderResponse struct {
// Body is an OpenAPI document as []byte or string.
Body any `cbor:"body" json:"body"`
ContentType string `cbor:"content_type,omitempty" json:"content_type,omitempty"`
}
LoaderResponse is returned by a "loader" hook plugin. Body must contain an OpenAPI document in JSON or YAML form. ContentType may describe that returned body when it differs from the input document's content type.
type Manifest ¶
type Manifest struct {
// Name is the stable plugin identifier, without the "restish-" executable prefix.
Name string `cbor:"name" json:"name"`
// Version is a plugin-defined version string shown in plugin listings.
Version string `cbor:"version,omitempty" json:"version,omitempty"`
// Description is a short human-readable summary of the plugin.
Description string `cbor:"description,omitempty" json:"description,omitempty"`
// RestishAPIVersion is the minimum host/plugin protocol version required by
// this plugin. Restish treats future protocol versions as backward
// compatible unless RequiredFeatures asks for unsupported behavior.
RestishAPIVersion int `cbor:"restish_api_version" json:"restish_api_version"`
// Hooks lists plugin capabilities such as "command", "formatter", or "auth".
Hooks []string `cbor:"hooks,omitempty" json:"hooks,omitempty"`
// RequiredFeatures lists additive protocol features that must be supported
// by the host before this plugin may run. Unknown optional manifest fields
// are ignored, but unknown required features fail manifest loading.
RequiredFeatures []string `cbor:"required_features,omitempty" json:"required_features,omitempty"`
// FormatterNames lists the output format names this plugin registers when
// the "formatter" hook is declared.
FormatterNames []string `cbor:"formatter_names,omitempty" json:"formatter_names,omitempty"`
// LoaderContentTypes lists the MIME types this plugin handles when the
// "loader" hook is declared.
LoaderContentTypes []string `cbor:"loader_content_types,omitempty" json:"loader_content_types,omitempty"`
// AuthAPINames, when non-empty, restricts the "auth" hook to the listed
// API names so the plugin is not invoked for every unrelated API.
AuthAPINames []string `cbor:"auth_api_names,omitempty" json:"auth_api_names,omitempty"`
// NeedsAuthSecrets, when true, tells Restish to forward secret auth
// params (passwords, client secrets) and credential-bearing request headers
// to this plugin. When false (the default), secret params are omitted and
// Authorization, Cookie, and Proxy-Authorization request headers are sent
// as "<redacted>" in auth and middleware hook payloads.
NeedsAuthSecrets bool `cbor:"needs_auth_secrets,omitempty" json:"needs_auth_secrets,omitempty"`
// HookTimeouts overrides the per-hook subprocess deadline. Keys are hook
// names (e.g. "auth", "request-middleware"). The default is 30 s for all
// hooks except "auth", which defaults to 5 minutes.
HookTimeouts map[string]time.Duration `cbor:"hook_timeouts,omitempty" json:"hook_timeouts,omitempty"`
}
Manifest is the metadata a plugin reports when called with --rsh-plugin-manifest. Plugin authors populate and write this with WriteManifest instead of manually marshalling CBOR.
type ProgressMsg ¶
ProgressMsg prints an informational progress line on the host stderr.
type PromptMsg ¶
type PromptMsg struct {
Type string `cbor:"type"`
RequestID string `cbor:"request_id,omitempty"`
Message string `cbor:"message"`
Hidden bool `cbor:"hidden,omitempty"`
}
PromptMsg asks the host to display a message and read one line from the user. When Hidden is true and stdin is a TTY, echo is suppressed (suitable for password entry).
type PromptResponseMsg ¶
type PromptResponseMsg struct {
Type string `cbor:"type"`
RequestID string `cbor:"request_id,omitempty"`
Value string `cbor:"value,omitempty"`
Error string `cbor:"error,omitempty"`
}
PromptResponseMsg is the host reply to a PromptMsg.
type RequestMiddlewareInput ¶
type RequestMiddlewareInput struct {
Type string `cbor:"type" json:"type"`
Request HookRequest `cbor:"request" json:"request"`
}
RequestMiddlewareInput is sent to plugins registered for the "request-middleware" hook.
type RequestMiddlewareOutput ¶
type RequestMiddlewareOutput struct {
Request *HookRequestHeaderUpdate `cbor:"request,omitempty" json:"request,omitempty"`
}
RequestMiddlewareOutput is the reply from a "request-middleware" hook plugin.
type ResponseMiddlewareInput ¶
type ResponseMiddlewareInput struct {
Type string `cbor:"type" json:"type"`
Request HookRequest `cbor:"request" json:"request"`
Response HookResponse `cbor:"response" json:"response"`
}
ResponseMiddlewareInput is sent to plugins registered for the "response-middleware" hook.
type ResponseMiddlewareOutput ¶
type ResponseMiddlewareOutput struct {
Drop bool `cbor:"drop,omitempty" json:"drop,omitempty"`
Follow *FollowRequest `cbor:"follow,omitempty" json:"follow,omitempty"`
Response *HookResponseUpdate `cbor:"response,omitempty" json:"response,omitempty"`
}
ResponseMiddlewareOutput is the reply from a "response-middleware" hook plugin.
type ResponseMsg ¶
type ResponseMsg struct {
Type string `cbor:"type"`
Status int `cbor:"status,omitempty"`
Headers map[string][]string `cbor:"headers,omitempty"`
Body any `cbor:"body,omitempty"`
}
ResponseMsg asks the host to format and display a response using the configured output formatter (same as a regular API response).
type SpinnerMsg ¶
SpinnerMsg requests spinner-style status text on the host stderr.
type StderrDataMsg ¶
StderrDataMsg sends a chunk of raw bytes to the host's stderr.
type StdinCloseMsg ¶
type StdinCloseMsg struct {
Type string `cbor:"type"`
}
StdinCloseMsg signals that the host's stdin has reached EOF.
type StdinDataMsg ¶
StdinDataMsg carries a chunk of stdin bytes from the host to the plugin (passthrough_stdio mode).
type StdoutDataMsg ¶
StdoutDataMsg sends a chunk of raw bytes to the host's stdout.
type TLSSignerInitMsg ¶
TLSSignerInitMsg is sent by the host to a tls-signer plugin at startup. The Type field is MsgTypeInit ("init").
type TLSSignerReadyMsg ¶
TLSSignerReadyMsg is sent by the plugin when it has loaded its certificate and is ready to sign. Type is MsgTypeTLSSignerReady ("ready").
type TLSSignerShutdownMsg ¶
type TLSSignerShutdownMsg struct {
Type string `cbor:"type"`
}
TLSSignerShutdownMsg asks the plugin to release resources and exit.
type TLSSignerSignMsg ¶
type TLSSignerSignMsg struct {
Type string `cbor:"type"`
Digest []byte `cbor:"digest"`
// Hash is the crypto.Hash value cast to uint64. 0 means no specific hash.
Hash uint64 `cbor:"hash,omitempty"`
Padding string `cbor:"padding,omitempty"`
SaltLength int `cbor:"salt_length,omitempty"`
}
TLSSignerSignMsg is sent by the host to request a signature. Type is MsgTypeTLSSignerSign ("sign").
type TLSSignerSignedMsg ¶
type TLSSignerSignedMsg struct {
Signature []byte `cbor:"signature,omitempty"`
Error string `cbor:"error,omitempty"`
}
TLSSignerSignedMsg is the plugin's reply to a TLSSignerSignMsg. It carries either a Signature or an Error, never both. This message has no Type field.
type TerminalContext ¶
type TerminalContext struct {
// Color is true when the host terminal supports ANSI colour sequences.
Color bool
// StdoutTTY is true when the host's stdout is connected to a terminal.
StdoutTTY bool
// StderrTTY is true when the host's stderr is connected to a terminal.
StderrTTY bool
// Theme is the host's configured Restish terminal theme entries.
Theme map[string]string
}
TerminalContext carries terminal capability flags that the Restish host passes to command plugins as CLI arguments. Plugins that care about colour or TTY detection should call TerminalContextFromArgs(os.Args[1:]).
func TerminalContextFromArgs ¶
func TerminalContextFromArgs(args []string) TerminalContext
TerminalContextFromArgs parses the Restish-injected terminal flags from the given argument slice (typically os.Args[1:]) and returns a TerminalContext. Unrecognised arguments are silently ignored.
ctx := plugin.TerminalContextFromArgs(os.Args[1:])