Documentation
¶
Index ¶
- type ConfigDiff
- type DirWatcher
- type Handler
- func (h *Handler) CurrentConfig() *config.Stack
- func (h *Handler) Initialize(ctx context.Context, stackPath string) (*ReloadResult, error)
- func (h *Handler) Reload(ctx context.Context) (*ReloadResult, error)
- func (h *Handler) SetLogger(logger *slog.Logger)
- func (h *Handler) SetNoExpand(noExpand bool)
- func (h *Handler) SetOnConfigApplied(fn func(*config.Stack))
- func (h *Handler) SetRegisterServerFunc(...)
- type MCPServerChange
- type MCPServerDiff
- type ReloadResult
- type ReplicaRuntime
- type ResourceChange
- type ResourceDiff
- type Watcher
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type ConfigDiff ¶
type ConfigDiff struct {
MCPServers MCPServerDiff
Resources ResourceDiff
// NetworkChanged indicates if the network config changed (requires full restart)
NetworkChanged bool
// ClientsChanged indicates the per-client access (`clients:`) block changed.
// It needs an in-memory policy refresh (via the reload's onConfigApplied hook)
// but no container or network work, so it must still mark the diff non-empty.
ClientsChanged bool
// ModelAttributionChanged indicates the server and/or client model
// mappings used for cost attribution changed (a server's `model:`, the
// gateway's `default_model:`, or a `client_models:` entry). Like
// ClientsChanged it needs only an in-memory refresh via the
// onConfigApplied hook — pricing metadata never warrants a container
// restart — but it must still mark the diff non-empty.
ModelAttributionChanged bool
// LimitsChanged indicates the budget/rate-limit (`limits:`) block
// changed. Like ClientsChanged it needs only an in-memory policy rebuild
// via the onConfigApplied hook (current-window spend carries over for
// unchanged entries) but must still mark the diff non-empty.
LimitsChanged bool
// GroupsChanged indicates the tool-group (`groups:`) block changed.
// Like ClientsChanged it needs only an in-memory policy rebuild via the
// onConfigApplied hook but must still mark the diff non-empty.
GroupsChanged bool
}
ConfigDiff represents the differences between two stack configurations.
func ComputeDiff ¶
func ComputeDiff(old, new *config.Stack) *ConfigDiff
ComputeDiff computes the differences between two stack configurations.
func (*ConfigDiff) IsEmpty ¶
func (d *ConfigDiff) IsEmpty() bool
IsEmpty returns true if there are no changes.
type DirWatcher ¶
type DirWatcher struct {
// contains filtered or unexported fields
}
DirWatcher monitors a directory tree for changes and triggers a debounced callback. Unlike Watcher, which targets a single file, DirWatcher watches a whole subtree (e.g. the registry skills directory, where each skill is its own directory containing SKILL.md plus optional supporting files).
fsnotify is not recursive, so the watcher adds the root and every existing subdirectory, and adds newly-created subdirectories as it observes them. The callback is expected to re-read the tree from disk (a full reload), so the watcher does not need to capture every individual event — it only needs to fire shortly after a burst of changes settles.
func NewDirWatcher ¶
func NewDirWatcher(root string, onChange func() error) *DirWatcher
NewDirWatcher creates a recursive directory watcher rooted at root. onChange is called (after debouncing) whenever an entry under root is created, written, removed, or renamed.
func (*DirWatcher) SetDebounce ¶
func (w *DirWatcher) SetDebounce(d time.Duration)
SetDebounce sets the debounce duration for change events.
func (*DirWatcher) SetLogger ¶
func (w *DirWatcher) SetLogger(logger *slog.Logger)
SetLogger sets the logger for watcher events.
func (*DirWatcher) Watch ¶
func (w *DirWatcher) Watch(ctx context.Context) error
Watch starts watching the directory tree for changes. It blocks until the context is cancelled.
The root may not exist yet (the registry skills directory is created lazily). Watch never creates it — that is the caller's job — and instead watches the nearest existing ancestor so the root's eventual creation is observed and the subtree armed. Staying write-free keeps the watcher safe to run against a directory another goroutine may be removing (e.g. a test's temp dir).
type Handler ¶
type Handler struct {
// contains filtered or unexported fields
}
Handler manages hot reload for a running stack.
func NewHandler ¶
func NewHandler(stackPath string, currentCfg *config.Stack, gateway *mcp.Gateway, rt *runtime.Orchestrator, port, basePort int, v config.VaultLookup, vs config.VaultSetLookup) *Handler
NewHandler creates a reload handler.
func (*Handler) CurrentConfig ¶
CurrentConfig returns the current stack configuration.
func (*Handler) Initialize ¶
Initialize cold-loads a stack into a running stackless daemon. It sets the stack path, resets currentCfg to nil so that ComputeDiff treats every server and resource as newly added, then calls Reload.
func (*Handler) Reload ¶
func (h *Handler) Reload(ctx context.Context) (*ReloadResult, error)
Reload reloads the configuration from disk and applies changes.
func (*Handler) SetNoExpand ¶
SetNoExpand sets whether to skip OpenAPI env var expansion.
func (*Handler) SetOnConfigApplied ¶
SetOnConfigApplied registers a hook fired after a successful reload swaps currentCfg. Today only the telemetry persistence layer hooks this — it uses the new stack to refresh per-server file writers without restarting the gateway. The hook runs while the handler still holds its mutex, so callbacks must be quick and must not call back into Handler.
func (*Handler) SetRegisterServerFunc ¶
func (h *Handler) SetRegisterServerFunc(fn func(ctx context.Context, server config.MCPServer, replicas []ReplicaRuntime, stackPath string) error)
SetRegisterServerFunc sets the callback for registering MCP servers.
type MCPServerChange ¶
MCPServerChange represents a modification to an existing MCP server.
type MCPServerDiff ¶
type MCPServerDiff struct {
Added []config.MCPServer
Removed []config.MCPServer
Modified []MCPServerChange
// AutoscalePolicyChanges lists servers whose autoscale block fields
// changed but whose other config is stable. The reload handler applies
// these via Autoscaler.UpdatePolicy without restarting the server.
AutoscalePolicyChanges []MCPServerChange
}
MCPServerDiff contains changes to MCP servers.
type ReloadResult ¶
type ReloadResult struct {
Success bool `json:"success"`
Message string `json:"message"`
Added []string `json:"added,omitempty"`
Removed []string `json:"removed,omitempty"`
Modified []string `json:"modified,omitempty"`
Errors []string `json:"errors,omitempty"`
}
ReloadResult contains the result of a reload operation.
type ReplicaRuntime ¶
ReplicaRuntime carries runtime handles for one replica that the reload handler has provisioned. Container replicas populate both fields; local- process, SSH, external, and OpenAPI replicas pass zero-valued entries.
type ResourceChange ¶
ResourceChange represents a modification to an existing resource.
type ResourceDiff ¶
type ResourceDiff struct {
Added []config.Resource
Removed []config.Resource
Modified []ResourceChange
}
ResourceDiff contains changes to resources.
type Watcher ¶
type Watcher struct {
// contains filtered or unexported fields
}
Watcher monitors a stack file for changes and triggers reload.
func NewWatcher ¶
NewWatcher creates a file watcher for the given stack path. onChange is called when the file changes (after debouncing).
func (*Watcher) SetDebounce ¶
SetDebounce sets the debounce duration for file changes.
func (*Watcher) Watch ¶
Watch starts watching the file for changes. Blocks until context is cancelled.
We watch the parent directory rather than the file directly because most editors use atomic saves (write to temp file, then rename). When a file is renamed over the watched file, fsnotify loses track of it. Watching the directory catches all events including renames.