reload

package
v0.1.0-beta.13 Latest Latest
Warning

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

Go to latest
Published: Jun 30, 2026 License: Apache-2.0 Imports: 15 Imported by: 0

Documentation

Index

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
}

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

func (h *Handler) CurrentConfig() *config.Stack

CurrentConfig returns the current stack configuration.

func (*Handler) Initialize

func (h *Handler) Initialize(ctx context.Context, stackPath string) (*ReloadResult, error)

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) SetLogger

func (h *Handler) SetLogger(logger *slog.Logger)

SetLogger sets the logger.

func (*Handler) SetNoExpand

func (h *Handler) SetNoExpand(noExpand bool)

SetNoExpand sets whether to skip OpenAPI env var expansion.

func (*Handler) SetOnConfigApplied

func (h *Handler) SetOnConfigApplied(fn func(*config.Stack))

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

type MCPServerChange struct {
	Name string
	Old  config.MCPServer
	New  config.MCPServer
}

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

type ReplicaRuntime struct {
	HostPort    int
	ContainerID string
}

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

type ResourceChange struct {
	Name string
	Old  config.Resource
	New  config.Resource
}

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

func NewWatcher(path string, onChange func() error) *Watcher

NewWatcher creates a file watcher for the given stack path. onChange is called when the file changes (after debouncing).

func (*Watcher) SetDebounce

func (w *Watcher) SetDebounce(d time.Duration)

SetDebounce sets the debounce duration for file changes.

func (*Watcher) SetLogger

func (w *Watcher) SetLogger(logger *slog.Logger)

SetLogger sets the logger for watcher events.

func (*Watcher) Watch

func (w *Watcher) Watch(ctx context.Context) error

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.

Jump to

Keyboard shortcuts

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