manager

package
v0.1.156 Latest Latest
Warning

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

Go to latest
Published: Apr 24, 2026 License: MIT Imports: 33 Imported by: 0

Documentation

Index

Constants

View Source
const AgentSourceEnv = "CODEFLY_AGENT_SOURCE"

AgentSourceEnv selects where "latest" agent versions are resolved from.

  • "" or "remote" (default): GitHub releases first, fall back to local.
  • "local": scan ~/.codefly/agents/ only, never call GitHub.

Set via the CLI's `--local-agents` persistent flag or by exporting CODEFLY_AGENT_SOURCE=local. Useful for offline work and for agent development where the local build is the source of truth.

View Source
const DefaultDialTimeout = 10 * time.Second

DefaultDialTimeout is how long Load waits for the gRPC connection to become ready after dialing.

View Source
const DefaultStartupTimeout = 30 * time.Second

DefaultStartupTimeout is how long Load waits for the agent to print its handshake line before giving up.

Variables

View Source
var (
	// ErrAgentNil is returned by Load when called with a nil agent ref.
	// Programmer error — should never reach the user.
	ErrAgentNil = errors.New("agent: nil reference")

	// ErrAgentBinaryNotFound is returned when the agent binary cannot be
	// resolved via local cache, NixStore, OCIStore, or GitHub.
	// Remediation: `codefly agent build`, set AGENT_NIX_FLAKE /
	// AGENT_REGISTRY, or check publisher/name/version in the agent ref.
	ErrAgentBinaryNotFound = errors.New("agent: binary not found")

	// ErrAgentSpawn is returned when exec.Cmd.Start fails. Almost always
	// a permissions issue (binary not executable) or kernel exec failure.
	ErrAgentSpawn = errors.New("agent: spawn failed")

	// ErrAgentHandshakeTimeout is returned when the agent process did
	// not print its VERSION|PORT handshake within startupTimeout.
	// Remediation: check the agent's stderr (via AgentConn.StderrTail
	// after Close, or pass a logWriter) for the panic / startup error.
	ErrAgentHandshakeTimeout = errors.New("agent: handshake timeout")

	// ErrAgentHandshakeMalformed is returned when the agent emitted a
	// first stdout line that doesn't parse as VERSION|PORT.
	ErrAgentHandshakeMalformed = errors.New("agent: handshake malformed")

	// ErrAgentVersionMismatch fires when the agent advertises a protocol
	// version this loader doesn't speak. Remediation: rebuild agent
	// against current core, or upgrade core.
	ErrAgentVersionMismatch = errors.New("agent: protocol version mismatch")

	// ErrAgentDialTimeout is returned when the gRPC connection to the
	// spawned agent didn't reach Ready within dialTimeout. The agent is
	// listening on the announced port but isn't accepting connections —
	// usually a server misconfiguration or a TLS mismatch.
	ErrAgentDialTimeout = errors.New("agent: gRPC dial timeout")
)

Sentinel errors for agent loading failures. Callers (CLI, MCP server, daemon) should switch on these via errors.Is to pick the right user message and remediation.

Each is wrapped on the way out — the underlying error chain still carries the original cause + stderr tail for diagnostics. errors.Is matches through the chain.

View Source
var (
	// ErrStoreUnavailable is returned by Available when the configured
	// store cannot be reached (network down, registry refusing).
	ErrStoreUnavailable = errors.New("store: unavailable")

	// ErrStoreArtifactMissing is returned by Pull when the agent ref
	// resolves to nothing in the store. Distinct from ErrStoreUnavailable
	// to let callers retry-with-backoff vs fail-fast appropriately.
	ErrStoreArtifactMissing = errors.New("store: artifact missing")
)

Sentinels for the AgentStore implementations.

Functions

func AgentSourceLocal added in v0.1.155

func AgentSourceLocal() bool

AgentSourceLocal returns true when the agent loader should bypass GitHub and resolve versions exclusively from the local agent directory. See AgentSourceEnv.

func Cleanup

func Cleanup(unique string)

Cleanup kills an agent process by its unique key and removes it from the active map.

func CleanupAll added in v0.1.155

func CleanupAll()

CleanupAll closes every active agent connection. Call this during graceful daemon shutdown.

func CopyFile added in v0.1.132

func CopyFile(src, dst string) error

CopyFile copies a file from src to dst. If dst does not exist, it is created with permissions copied from src.

func Download

func Download(ctx context.Context, p *resources.Agent) error

func DownloadURL

func DownloadURL(p *resources.Agent) string

func Downloaded added in v0.0.90

func Downloaded(ctx context.Context, p *resources.Agent) (bool, error)

func FindLocalLatest added in v0.1.155

func FindLocalLatest(ctx context.Context, agent *resources.Agent) error

FindLocalLatest scans the local agent directory for installed binaries matching the agent name and returns the highest semver version found. This is the preferred resolution path for locally-built agents (via "codefly agent build") that have no GitHub release.

func MoveFile added in v0.1.132

func MoveFile(src, dst string) error

MoveFile attempts to rename the file, and if it fails due to an invalid cross-device link, it falls back to copying the file and then removing the original file.

func PinToLatestRelease

func PinToLatestRelease(ctx context.Context, agent *resources.Agent) error

PinToLatestRelease queries GitHub for the latest release tag and updates the agent's version. Falls back to FindLocalLatest if GitHub is unreachable or has no releases for this agent.

When CODEFLY_AGENT_SOURCE=local (or --local-agents on the CLI), GitHub is skipped entirely and resolution goes straight to the local filesystem scan. This makes "version: latest" work offline and lets agent developers iterate on locally-built binaries without needing to cut a GitHub release.

func PlatformSuffix added in v0.1.155

func PlatformSuffix() string

PlatformSuffix returns the OS/arch suffix for the current platform. Used when pushing platform-specific agent binaries.

func ResolveLatest added in v0.1.155

func ResolveLatest(ctx context.Context, agent *resources.Agent) error

ResolveLatest resolves agent.Version when it is "latest". Strategy:

  1. If CODEFLY_AGENT_SOURCE=local: scan the local agent dir only.
  2. Otherwise: try FindLocalLatest first; if it succeeds, use it. This makes locally-built agents (via `codefly agent build`) take precedence over any GitHub release, which is the intent of running `codefly` from a dev checkout.
  3. Fall back to PinToLatestRelease (GitHub → local fallback).

No-op when agent.Version is already a concrete semver.

func ValidURL

func ValidURL(s string) bool

Types

type AgentConn added in v0.1.155

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

AgentConn is a connection to a running agent process. It owns the gRPC connection and the child process.

func Load

func Load(ctx context.Context, p *resources.Agent, opts ...LoadOption) (*AgentConn, error)

Load spawns an agent binary, reads the gRPC port from its stdout, and establishes a gRPC connection. The agent binary is downloaded if not already present.

func (*AgentConn) Close added in v0.1.155

func (c *AgentConn) Close()

Close shuts down the gRPC connection, asks the agent to exit gracefully (SIGTERM) so it can reap its child processes (user binaries, Docker containers) via its agents.Serve signal handler, then falls back to SIGKILL if the agent is unresponsive.

The previous implementation jumped straight to SIGKILL, which won the race against the agent's own SIGTERM handler and orphaned every child process as a PPID=1 zombie — exactly what the agent's signal handler was written to prevent.

cmd.Wait must only be called once — the reaper owns it. We observe completion via the `done` channel the reaper closes.

func (*AgentConn) GRPCConn added in v0.1.155

func (c *AgentConn) GRPCConn() *grpc.ClientConn

GRPCConn returns the shared gRPC connection to the agent.

func (*AgentConn) ProcessInfo added in v0.1.155

func (c *AgentConn) ProcessInfo() *ProcessInfo

ProcessInfo returns the agent's process metadata.

func (*AgentConn) StderrTail added in v0.1.155

func (c *AgentConn) StderrTail() string

StderrTail returns the last captured bytes from the agent's stderr. Useful for diagnostics after a crash or handshake failure.

type AgentStore added in v0.1.155

type AgentStore interface {
	// Pull downloads an agent binary and returns the local file path.
	// The binary is cached locally — subsequent calls for the same name+version
	// return the cached path without re-downloading.
	Pull(ctx context.Context, agent *resources.Agent) (binaryPath string, err error)

	// Available checks if an agent exists in the store without downloading it.
	Available(ctx context.Context, agent *resources.Agent) (bool, error)
}

AgentStore provides a mechanism to pull agent binaries from a remote registry. Implementations: OCIStore (OCI-compliant registries), HTTPStore (simple HTTP), NixStore (content-addressed via Nix flakes).

type GithubSource

type GithubSource struct {
	Owner string
	Repo  string
}

type LoadOption added in v0.1.155

type LoadOption func(*loadConfig)

LoadOption configures optional behaviour of Load.

func WithDialTimeout added in v0.1.155

func WithDialTimeout(d time.Duration) LoadOption

WithDialTimeout overrides the default time Load waits for the gRPC connection to become ready.

func WithLogWriter added in v0.1.155

func WithLogWriter(w io.Writer) LoadOption

WithLogWriter tees the agent's stderr to w in real time, in addition to buffering in the ring buffer. Useful for debug mode where the gateway wants to surface agent logs.

func WithStartupTimeout added in v0.1.155

func WithStartupTimeout(d time.Duration) LoadOption

WithStartupTimeout overrides the default time Load waits for the agent handshake.

func WithWorkDir added in v0.1.155

func WithWorkDir(dir string) LoadOption

WithWorkDir sets the working directory for the agent process and exports CODEFLY_AGENT_WORKDIR so the agent can resolve file paths.

type NixStore added in v0.1.155

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

NixStore pulls agent binaries by realizing a Nix flake output.

Motivation: OCI/HTTP stores cache by `{publisher}/{name}:{version}` — a mutable tag in a registry. Nix outputs are content-addressed, so the same flake ref always yields the same store path; cache invalidation is automatic and cross-platform selection is handled by Nix itself (no PlatformSuffix hand-waving).

Layout convention: the configured flake must expose agents at `packages.${system}.agents-{kind}-{name}-{version}`. The build output must be either:

  • a file that is the agent binary, or
  • a directory containing `bin/service-{name}` (the nixpkgs convention for a package built from a Go module).

Configure via env:

AGENT_NIX_FLAKE=github:codefly-dev/codefly       # ref
AGENT_NIX_FLAKE=github:codefly-dev/codefly/v0.5  # ref pinned to a tag
AGENT_NIX_FLAKE=/path/to/local/flake             # local checkout

If unset, NewNixStoreFromEnv returns nil.

func NewNixStore added in v0.1.155

func NewNixStore(flakeRef string, logger *slog.Logger) *NixStore

NewNixStore creates a NixStore for an explicit flake ref.

func NewNixStoreFromEnv added in v0.1.155

func NewNixStoreFromEnv(logger *slog.Logger) *NixStore

NewNixStoreFromEnv creates a NixStore from AGENT_NIX_FLAKE. Returns nil if unset or if `nix` is not on PATH.

func (*NixStore) Available added in v0.1.155

func (s *NixStore) Available(ctx context.Context, agent *resources.Agent) (bool, error)

Available asks nix to evaluate the attribute without building it. Cheap compared to Pull: just flake eval, no derivation realization.

func (*NixStore) Pull added in v0.1.155

func (s *NixStore) Pull(ctx context.Context, agent *resources.Agent) (string, error)

Pull realizes the flake output and returns the path of the agent binary. Nix handles its own content-addressed cache under /nix/store, so repeat Pulls for the same ref are no-ops once the derivation is realized.

type OCIStore added in v0.1.155

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

OCIStore pulls agent binaries from an OCI-compliant registry. Agent binaries are stored as single-layer OCI artifacts.

Registry layout:

{registry}/agents/{publisher}/{name}:{version}
  └── single layer: service-{name} (the binary, for current OS/arch)

Push example:

oras push localhost:5111/agents/codefly.dev/go-generic:0.0.1 \
  service-go-generic:application/octet-stream

The store uses the OCI distribution spec via plain HTTP calls (no oras CLI needed). For simplicity, we use the Docker Registry HTTP API V2.

func NewOCIStore added in v0.1.155

func NewOCIStore(registry, scheme string, logger *slog.Logger) *OCIStore

NewOCIStore creates a store backed by an OCI registry. The registry should be the base URL without scheme (e.g., "localhost:5111"). Use "http" scheme for local k3d registries, "https" for production.

func NewOCIStoreFromEnv added in v0.1.155

func NewOCIStoreFromEnv(logger *slog.Logger) *OCIStore

NewOCIStoreFromEnv creates an OCIStore from the AGENT_REGISTRY env var. Returns nil if AGENT_REGISTRY is not set.

Format: AGENT_REGISTRY=localhost:5111 (local k3d)

AGENT_REGISTRY=ghcr.io/codefly-dev (production)

func (*OCIStore) Available added in v0.1.155

func (s *OCIStore) Available(ctx context.Context, agent *resources.Agent) (bool, error)

Available checks if the agent manifest exists in the registry.

func (*OCIStore) Pull added in v0.1.155

func (s *OCIStore) Pull(ctx context.Context, agent *resources.Agent) (string, error)

Pull downloads the agent binary from the OCI registry. Uses the OCI distribution spec to fetch the single blob layer.

type ProcessInfo added in v0.0.67

type ProcessInfo struct {
	PID int
}

ProcessInfo carries metadata about the spawned agent process.

Jump to

Keyboard shortcuts

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