Documentation
¶
Overview ¶
Package extension provides the core extension framework for OpsKat. It loads WASM extensions, manages their lifecycle, and bridges them into the main application's tool, policy, and frontend systems.
pkg/extension/host.go
pkg/extension/host_capability.go
pkg/extension/host_default.go
pkg/extension/hostfn.go
pkg/extension/io_handle.go
pkg/extension/io_http.go
pkg/extension/runtime.go
Index ¶
- Constants
- Variables
- func IsBuiltinSnippetCategoryID(id string) bool
- func IsPrivateIP(ip net.IP) bool
- func LoadLocales(dir string) map[string]map[string]string
- func PasswordFieldsFromSchema(schema map[string]any) []string
- type ActionCancellation
- type ActionEventHandler
- type AssetConfigGetter
- type AssetTypeDef
- type Bridge
- func (b *Bridge) FindExtensionByTool(extName, toolName string) *Extension
- func (b *Bridge) GetAssetTypes() []ExtAssetType
- func (b *Bridge) GetDefaultPolicyGroups(assetType string) []string
- func (b *Bridge) GetExtensionByAssetType(assetType string) *Extension
- func (b *Bridge) GetExtensionPolicyGroups(extName, assetType string, assetID int64) []string
- func (b *Bridge) GetPolicyGroups() []ExtPolicyGroup
- func (b *Bridge) GetSkillMD(assetType string) string
- func (b *Bridge) GetSkillMDWithExtension(assetType string) SkillMDWithExtension
- func (b *Bridge) ListNames() []string
- func (b *Bridge) Register(ext *Extension)
- func (b *Bridge) Unregister(name string)
- type Capabilities
- type DefaultHostConfig
- type DefaultHostProvider
- func (h *DefaultHostProvider) ActionEvent(eventType string, data json.RawMessage) error
- func (h *DefaultHostProvider) ActionShouldStop() bool
- func (h *DefaultHostProvider) CloseAll()
- func (h *DefaultHostProvider) FileDialog(dialogType string, opts DialogOptions) (string, error)
- func (h *DefaultHostProvider) GetAssetConfig(assetID int64) (json.RawMessage, error)
- func (h *DefaultHostProvider) IOClose(handleID uint32) error
- func (h *DefaultHostProvider) IOFlush(handleID uint32) (*IOMeta, error)
- func (h *DefaultHostProvider) IOOpen(params IOOpenParams) (uint32, IOMeta, error)
- func (h *DefaultHostProvider) IORead(handleID uint32, size int) ([]byte, error)
- func (h *DefaultHostProvider) IOSetDeadline(handleID uint32, kind string, unixNanos int64) error
- func (h *DefaultHostProvider) IOWrite(handleID uint32, data []byte) (int, error)
- func (h *DefaultHostProvider) KVGet(key string) ([]byte, error)
- func (h *DefaultHostProvider) KVSet(key string, value []byte) error
- func (h *DefaultHostProvider) Log(level, msg string)
- func (h *DefaultHostProvider) SetActiveCancellation(c *ActionCancellation)
- type DialFunc
- type DialogOptions
- type ExtAssetType
- type ExtPolicyGroup
- type Extension
- type FSCapability
- type FileDialogOpener
- type FrontendDef
- type HTTPCapability
- type HostProvider
- type I18nDesc
- type I18nName
- type I18nNameDesc
- type IOHandleManager
- func (m *IOHandleManager) Close(id uint32) error
- func (m *IOHandleManager) CloseAll()
- func (m *IOHandleManager) Flush(id uint32) (*IOMeta, error)
- func (m *IOHandleManager) GetMeta(id uint32) (IOMeta, error)
- func (m *IOHandleManager) OpenFile(path string, mode string) (uint32, IOMeta, error)
- func (m *IOHandleManager) OpenHTTP(params IOOpenParams, dial DialFunc) (uint32, IOMeta, error)
- func (m *IOHandleManager) Read(id uint32, buf []byte) (int, error)
- func (m *IOHandleManager) Register(r io.Reader, w io.Writer, c io.Closer, meta IOMeta) (uint32, error)
- func (m *IOHandleManager) SetDeadline(id uint32, kind string, t time.Time) error
- func (m *IOHandleManager) Write(id uint32, data []byte) (int, error)
- type IOMeta
- type IOOpenParams
- type KVStore
- type Manager
- func (m *Manager) Close(ctx context.Context)
- func (m *Manager) ExtDir(name string) string
- func (m *Manager) GetExtension(name string) *Extension
- func (m *Manager) Install(ctx context.Context, sourcePath string) (*Manifest, error)
- func (m *Manager) ListExtensions() []*Extension
- func (m *Manager) LoadExtension(ctx context.Context, dir string) (*Manifest, error)
- func (m *Manager) Scan(ctx context.Context) ([]*Manifest, error)
- func (m *Manager) ScanManifests() ([]*ManifestInfo, error)
- func (m *Manager) Shutdown(ctx context.Context)
- func (m *Manager) Uninstall(ctx context.Context, name string) error
- func (m *Manager) Unload(ctx context.Context, name string) error
- func (m *Manager) Watch(ctx context.Context, onChange func()) error
- type Manifest
- func (m *Manifest) CheckCredentialRead() error
- func (m *Manifest) CheckFSRead(path, extDir string) error
- func (m *Manifest) CheckFSWrite(path, extDir string) error
- func (m *Manifest) CheckHTTPURL(urlStr string, tunnelAllowed bool) error
- func (m *Manifest) CheckTunnel() error
- func (m *Manifest) Localized(tr func(key string) string) *Manifest
- type ManifestBackend
- type ManifestI18n
- type ManifestInfo
- type PageDef
- type Plugin
- func (p *Plugin) CallAction(ctx context.Context, actionName string, args json.RawMessage) (json.RawMessage, error)
- func (p *Plugin) CallTool(ctx context.Context, toolName string, args json.RawMessage) (json.RawMessage, error)
- func (p *Plugin) CancelActiveAction()
- func (p *Plugin) CheckPolicy(ctx context.Context, toolName string, args json.RawMessage) (action, resource string, err error)
- func (p *Plugin) Close(ctx context.Context) error
- func (p *Plugin) Manifest() *Manifest
- func (p *Plugin) ValidateConfig(ctx context.Context, config json.RawMessage) ([]ValidationError, error)
- type PoliciesDef
- type PolicyGroupDef
- type SeedSnippetDef
- type SkillMDWithExtension
- type SnippetCategoryDef
- type SnippetsDef
- type ToolDef
- type TunnelDialer
- type ValidationError
Constants ¶
const ( CredentialAccessNone = "" CredentialAccessRead = "read" )
CredentialAccessNone means the extension receives only opaque credential handles (default). CredentialAccessRead means the extension may request plaintext credentials (discouraged).
const HostABIVersion = "1.0"
HostABIVersion is the current host ABI contract version. Extensions must declare a compatible hostABI in their manifest. Bump the minor version when adding new host functions (backward compatible); bump the major version when removing or changing existing host function signatures.
Variables ¶
var SupportedHostABIs = []string{"1.0"}
SupportedHostABIs lists all host ABI versions the runtime accepts.
Functions ¶
func IsBuiltinSnippetCategoryID ¶ added in v1.4.0
IsBuiltinSnippetCategoryID reports whether id is reserved by the app core.
func IsPrivateIP ¶
IsPrivateIP returns true for RFC1918, loopback, link-local, unspecified, IPv6 ULA, IPv6 link-local, and AWS/GCP metadata endpoints. Exported so that the dial-time guard in io_http.go can reuse it.
func LoadLocales ¶
LoadLocales reads all JSON files from the extension's locales/ directory. Language codes are normalized to lowercase for consistent matching (e.g. "zh-CN" → "zh-cn").
func PasswordFieldsFromSchema ¶
PasswordFieldsFromSchema extracts property names that have "format": "password" from a JSON Schema configSchema.
Types ¶
type ActionCancellation ¶ added in v1.4.0
type ActionCancellation struct {
// contains filtered or unexported fields
}
ActionCancellation is a flag polled by long-running actions via host_action_should_stop. Set by the host when the frontend requests cancel.
func NewActionCancellation ¶ added in v1.4.0
func NewActionCancellation() *ActionCancellation
func (*ActionCancellation) Cancel ¶ added in v1.4.0
func (c *ActionCancellation) Cancel()
Cancel marks the action as canceled. Idempotent.
func (*ActionCancellation) ShouldStop ¶ added in v1.4.0
func (c *ActionCancellation) ShouldStop() bool
ShouldStop returns true if Cancel has been called.
type ActionEventHandler ¶
type ActionEventHandler interface {
OnActionEvent(eventType string, data json.RawMessage) error
}
type AssetConfigGetter ¶
type AssetConfigGetter interface {
GetAssetConfig(assetID int64) (json.RawMessage, error)
}
Dependency interfaces for DefaultHostProvider
type AssetTypeDef ¶
type Bridge ¶
type Bridge struct {
// contains filtered or unexported fields
}
Bridge connects loaded extensions to the main app's tool, policy, and frontend systems.
func (*Bridge) FindExtensionByTool ¶
func (*Bridge) GetAssetTypes ¶
func (b *Bridge) GetAssetTypes() []ExtAssetType
func (*Bridge) GetDefaultPolicyGroups ¶
func (*Bridge) GetExtensionByAssetType ¶
GetExtensionByAssetType returns the Extension that registered the given asset type, or nil if no extension owns that type.
func (*Bridge) GetExtensionPolicyGroups ¶
func (*Bridge) GetPolicyGroups ¶
func (b *Bridge) GetPolicyGroups() []ExtPolicyGroup
func (*Bridge) GetSkillMD ¶
func (*Bridge) GetSkillMDWithExtension ¶
func (b *Bridge) GetSkillMDWithExtension(assetType string) SkillMDWithExtension
GetSkillMDWithExtension returns the SKILL.md content and extension name for an asset type. Returns empty struct if no SKILL.md is registered for the type.
func (*Bridge) Unregister ¶
type Capabilities ¶
type Capabilities struct {
FS FSCapability `json:"fs"`
HTTP HTTPCapability `json:"http"`
Credentials string `json:"credentials"` // "" (none) | "read"
Tunnel bool `json:"tunnel"` // allow routing HTTP through the asset's SSH tunnel
}
Capabilities declares what host resources an extension is permitted to access. Default (zero value) is deny-all except an implicit read-only ${EXT_DIR}/** for the extension's own directory. Extensions must declare every capability they need; the host enforces these at each host_* call site.
type DefaultHostConfig ¶
type DefaultHostConfig struct {
Logger *zap.Logger
AssetConfigs AssetConfigGetter
FileDialogs FileDialogOpener
KV KVStore
ActionEvents ActionEventHandler
TunnelDialer TunnelDialer // SSH tunnel dialer (nil = no tunnel support)
AssetSSHTunnelID int64 // Current asset's SSH tunnel ID (0 = direct)
}
type DefaultHostProvider ¶
type DefaultHostProvider struct {
// contains filtered or unexported fields
}
func NewDefaultHostProvider ¶
func NewDefaultHostProvider(cfg DefaultHostConfig) *DefaultHostProvider
func (*DefaultHostProvider) ActionEvent ¶
func (h *DefaultHostProvider) ActionEvent(eventType string, data json.RawMessage) error
func (*DefaultHostProvider) ActionShouldStop ¶ added in v1.4.0
func (h *DefaultHostProvider) ActionShouldStop() bool
func (*DefaultHostProvider) CloseAll ¶
func (h *DefaultHostProvider) CloseAll()
func (*DefaultHostProvider) FileDialog ¶
func (h *DefaultHostProvider) FileDialog(dialogType string, opts DialogOptions) (string, error)
func (*DefaultHostProvider) GetAssetConfig ¶
func (h *DefaultHostProvider) GetAssetConfig(assetID int64) (json.RawMessage, error)
func (*DefaultHostProvider) IOClose ¶
func (h *DefaultHostProvider) IOClose(handleID uint32) error
func (*DefaultHostProvider) IOFlush ¶
func (h *DefaultHostProvider) IOFlush(handleID uint32) (*IOMeta, error)
func (*DefaultHostProvider) IOOpen ¶
func (h *DefaultHostProvider) IOOpen(params IOOpenParams) (uint32, IOMeta, error)
func (*DefaultHostProvider) IORead ¶
func (h *DefaultHostProvider) IORead(handleID uint32, size int) ([]byte, error)
func (*DefaultHostProvider) IOSetDeadline ¶ added in v1.4.0
func (h *DefaultHostProvider) IOSetDeadline(handleID uint32, kind string, unixNanos int64) error
func (*DefaultHostProvider) IOWrite ¶
func (h *DefaultHostProvider) IOWrite(handleID uint32, data []byte) (int, error)
func (*DefaultHostProvider) KVSet ¶
func (h *DefaultHostProvider) KVSet(key string, value []byte) error
func (*DefaultHostProvider) Log ¶
func (h *DefaultHostProvider) Log(level, msg string)
func (*DefaultHostProvider) SetActiveCancellation ¶ added in v1.4.0
func (h *DefaultHostProvider) SetActiveCancellation(c *ActionCancellation)
type DialogOptions ¶
type ExtAssetType ¶
type ExtAssetType struct {
Type string
ExtensionName string
ConfigSchema map[string]any
I18n I18nName
ProxyChain bool
}
ExtAssetType represents an extension-provided asset type.
type ExtPolicyGroup ¶
type ExtPolicyGroup struct {
ID string
ExtensionName string
PolicyType string
I18n I18nNameDesc
Policy map[string]any
}
ExtPolicyGroup represents an extension-provided policy group.
type Extension ¶
type Extension struct {
Name string
Dir string
Manifest *Manifest
Plugin *Plugin
SkillMD string // Contents of SKILL.md
Locales map[string]map[string]string // lang → key → translated text
}
Extension represents a loaded extension.
type FSCapability ¶
FSCapability lists filesystem path patterns an extension may access. Patterns support one ${EXT_DIR} placeholder (resolved at load time to the extension's directory) and a trailing /** for recursive match. All other entries are treated as absolute path prefixes.
type FileDialogOpener ¶
type FileDialogOpener interface {
FileDialog(dialogType string, opts DialogOptions) (string, error)
}
type FrontendDef ¶
type HTTPCapability ¶
type HTTPCapability struct {
Allowlist []string `json:"allowlist"`
}
HTTPCapability lists URL prefix patterns an extension may open via host_io_open(type=http). Entries are URL prefixes; the request URL must start with at least one allowlist entry. Private-network destinations (RFC1918, loopback, link-local) are always rejected unless the target also matches an allowlist entry AND Tunnel=true.
type HostProvider ¶
type HostProvider interface {
IOOpen(params IOOpenParams) (uint32, IOMeta, error)
IORead(handleID uint32, size int) ([]byte, error)
IOWrite(handleID uint32, data []byte) (int, error)
IOFlush(handleID uint32) (*IOMeta, error)
IOClose(handleID uint32) error
// IOSetDeadline sets read/write/both deadline on a handle.
// unixNanos is an absolute deadline in Unix nanoseconds; 0 clears any existing deadline.
// kind ∈ {"read","write","both"}. Returns an error if the underlying handle type does not support deadlines.
IOSetDeadline(handleID uint32, kind string, unixNanos int64) error
GetAssetConfig(assetID int64) (json.RawMessage, error)
FileDialog(dialogType string, opts DialogOptions) (string, error)
Log(level, msg string)
KVGet(key string) ([]byte, error)
KVSet(key string, value []byte) error
ActionEvent(eventType string, data json.RawMessage) error
ActionShouldStop() bool
SetActiveCancellation(c *ActionCancellation)
CloseAll()
}
HostProvider defines the capabilities that the host provides to extensions. Main App and DevServer each provide their own implementation.
func NewCapabilityHost ¶
func NewCapabilityHost(inner HostProvider, manifest *Manifest, extDir string) HostProvider
NewCapabilityHost wraps inner with capability enforcement.
type I18nNameDesc ¶
type IOHandleManager ¶
type IOHandleManager struct {
// contains filtered or unexported fields
}
IOHandleManager manages IO handles for a single WASM invocation.
func NewIOHandleManager ¶
func NewIOHandleManager() *IOHandleManager
func (*IOHandleManager) Close ¶
func (m *IOHandleManager) Close(id uint32) error
func (*IOHandleManager) CloseAll ¶
func (m *IOHandleManager) CloseAll()
func (*IOHandleManager) Flush ¶
func (m *IOHandleManager) Flush(id uint32) (*IOMeta, error)
Flush flushes the HTTP handle (sends the request and waits for response).
func (*IOHandleManager) OpenHTTP ¶
func (m *IOHandleManager) OpenHTTP(params IOOpenParams, dial DialFunc) (uint32, IOMeta, error)
OpenHTTP creates an HTTP handle, wraps it with adapters, and stores it.
func (*IOHandleManager) Register ¶
func (m *IOHandleManager) Register(r io.Reader, w io.Writer, c io.Closer, meta IOMeta) (uint32, error)
Register adds an externally-created handle entry and returns its ID. Returns 0 and does not register if handle IDs are exhausted.
func (*IOHandleManager) SetDeadline ¶ added in v1.4.0
SetDeadline sets read/write deadline on a handle whose underlying resource supports it. kind ∈ {"read", "write", "both"}.
type IOMeta ¶
type IOMeta struct {
Size int64 `json:"size,omitempty"`
ContentType string `json:"contentType,omitempty"`
Status int `json:"status,omitempty"`
Headers map[string]string `json:"headers,omitempty"`
}
IOMeta contains metadata about an IO handle.
type IOOpenParams ¶
type IOOpenParams struct {
Type string `json:"type"`
Path string `json:"path"`
Mode string `json:"mode"`
Method string `json:"method"`
URL string `json:"url"`
Headers map[string]string `json:"headers"`
AllowPrivate bool `json:"allowPrivate"` // dial-time guard: allow connections to private/loopback IPs
// tcp (new)
Addr string `json:"addr,omitempty"`
Timeout int `json:"timeout,omitempty"` // ms; 0 = default 10s
}
type Manager ¶
type Manager struct {
// contains filtered or unexported fields
}
Manager handles extension discovery, loading, and lifecycle.
func NewManager ¶
func (*Manager) GetExtension ¶
func (*Manager) ListExtensions ¶
func (*Manager) LoadExtension ¶
func (*Manager) ScanManifests ¶
func (m *Manager) ScanManifests() ([]*ManifestInfo, error)
ScanManifests reads manifests from disk without loading WASM plugins.
type Manifest ¶
type Manifest struct {
Name string `json:"name"`
Version string `json:"version"`
Icon string `json:"icon"`
MinAppVersion string `json:"minAppVersion"`
HostABI string `json:"hostABI"`
Capabilities Capabilities `json:"capabilities"`
I18n ManifestI18n `json:"i18n"`
Backend ManifestBackend `json:"backend"`
AssetTypes []AssetTypeDef `json:"assetTypes"`
Tools []ToolDef `json:"tools"`
Policies PoliciesDef `json:"policies"`
Frontend FrontendDef `json:"frontend"`
Snippets SnippetsDef `json:"snippets"`
}
func ParseManifest ¶
func (*Manifest) CheckCredentialRead ¶
CheckCredentialRead returns nil if the extension is allowed to request plaintext credential values. Default is deny.
func (*Manifest) CheckFSRead ¶
CheckFSRead returns nil if reading the given absolute path is permitted by the extension's fs.read capabilities. extDir is the extension's own directory (used to resolve ${EXT_DIR} placeholders).
func (*Manifest) CheckFSWrite ¶
CheckFSWrite returns nil if writing to the given absolute path is permitted by the extension's fs.write capabilities.
func (*Manifest) CheckHTTPURL ¶
CheckHTTPURL returns nil if the given URL is permitted by the extension's http capabilities. Enforces:
- The URL prefix must match an allowlist entry.
- Private/loopback/link-local destinations are rejected unless tunnelAllowed is true.
- The scheme must be http or https.
tunnelAllowed should be true ONLY when the host has an active SSH tunnel configured AND the extension declared capabilities.tunnel=true.
func (*Manifest) CheckTunnel ¶
CheckTunnel returns nil if the extension is allowed to use SSH tunnel routing.
type ManifestBackend ¶
type ManifestI18n ¶
type ManifestInfo ¶
type ManifestInfo struct {
Name string
Dir string
Manifest *Manifest
Locales map[string]map[string]string
}
ManifestInfo holds manifest data for an extension that may not be loaded.
func LoadManifestInfo ¶
func LoadManifestInfo(dir string) (*ManifestInfo, error)
LoadManifestInfo reads a manifest from disk without loading the WASM plugin.
func (*ManifestInfo) Translate ¶
func (mi *ManifestInfo) Translate(lang, key string) string
Translate resolves an i18n key for the given language.
type Plugin ¶
type Plugin struct {
// contains filtered or unexported fields
}
Plugin represents a loaded WASM extension.
func LoadPlugin ¶
func LoadPlugin(ctx context.Context, manifest *Manifest, wasmBytes []byte, host HostProvider, cache wazero.CompilationCache) (*Plugin, error)
LoadPlugin compiles a WASM binary and prepares it for execution. If cache is non-nil, compiled modules are cached to disk for faster subsequent loads.
func (*Plugin) CallAction ¶
func (p *Plugin) CallAction(ctx context.Context, actionName string, args json.RawMessage) (json.RawMessage, error)
CallAction calls execute_action on the extension.
The cancellation is installed AFTER acquiring p.mu so that concurrent CallAction invocations don't race on host.activeCancel: the setup and the WASM execution live in the same critical section, and the defer clears the cancel before releasing the lock, so the next caller installs fresh state.
func (*Plugin) CallTool ¶
func (p *Plugin) CallTool(ctx context.Context, toolName string, args json.RawMessage) (json.RawMessage, error)
CallTool calls execute_tool on the extension.
func (*Plugin) CancelActiveAction ¶ added in v1.4.0
func (p *Plugin) CancelActiveAction()
CancelActiveAction triggers cancellation of the currently running action. Due to Plugin.mu serializing CallAction invocations, at most one action runs per plugin at a time — this cancels that one. No-op if idle.
func (*Plugin) CheckPolicy ¶
func (p *Plugin) CheckPolicy(ctx context.Context, toolName string, args json.RawMessage) (action, resource string, err error)
CheckPolicy calls check_policy on the extension.
func (*Plugin) ValidateConfig ¶
func (p *Plugin) ValidateConfig(ctx context.Context, config json.RawMessage) ([]ValidationError, error)
ValidateConfig calls validate_config on the extension.
type PoliciesDef ¶
type PoliciesDef struct {
Type string `json:"type"`
Actions []string `json:"actions"`
Groups []PolicyGroupDef `json:"groups"`
Default []string `json:"default"`
}
type PolicyGroupDef ¶
type PolicyGroupDef struct {
ID string `json:"id"`
I18n I18nNameDesc `json:"i18n"`
Policy map[string]any `json:"policy"`
}
type SeedSnippetDef ¶ added in v1.4.0
type SeedSnippetDef struct {
Key string `json:"key"`
Name string `json:"name"`
Category string `json:"category"`
Content string `json:"content"`
Description string `json:"description"`
}
SeedSnippetDef is a read-only snippet shipped with the extension.
Validation rules:
- Key must match ^[a-z0-9][a-z0-9-]{0,63}$ and be unique within this manifest.
- Name and Content must be non-empty after trimming.
- Category must be either a builtin category id or a category declared in THIS manifest.
type SkillMDWithExtension ¶
SkillMDWithExtension pairs SKILL.md content with its originating extension name.
type SnippetCategoryDef ¶ added in v1.4.0
type SnippetCategoryDef struct {
ID string `json:"id"`
AssetType string `json:"assetType"`
I18n I18nName `json:"i18n"`
}
SnippetCategoryDef declares a new snippet category id that this extension owns.
Validation rules:
- ID must match ^[a-z][a-z0-9-]{0,31}$ (lowercase kebab, <=32 chars).
- ID must NOT collide with any builtin category (see IsBuiltinSnippetCategoryID).
- ID must be unique within this manifest.
- AssetType must be non-empty and must match a type declared in assetTypes[].
type SnippetsDef ¶ added in v1.4.0
type SnippetsDef struct {
Categories []SnippetCategoryDef `json:"categories"`
Seed []SeedSnippetDef `json:"seed"`
}
SnippetsDef declares snippet categories and seed snippets contributed by this extension. All fields are optional; an empty block is a no-op.
type TunnelDialer ¶
TunnelDialer dials a TCP address through an SSH tunnel.
type ValidationError ¶
ValidationError represents a config validation error.