diff --git a/go/cli/cmd/kagent/main.go b/go/cli/cmd/kagent/main.go index 3dc4efd84..8a40533b0 100644 --- a/go/cli/cmd/kagent/main.go +++ b/go/cli/cmd/kagent/main.go @@ -3,6 +3,7 @@ package main import ( "context" "fmt" + "log" "os" "os/signal" "syscall" @@ -14,6 +15,7 @@ import ( "github.com/kagent-dev/kagent/go/cli/internal/profiles" "github.com/kagent-dev/kagent/go/cli/internal/tui" "github.com/spf13/cobra" + "github.com/spf13/viper" ) func main() { @@ -46,6 +48,12 @@ func main() { rootCmd.PersistentFlags().StringVarP(&cfg.OutputFormat, "output-format", "o", "table", "Output format") rootCmd.PersistentFlags().BoolVarP(&cfg.Verbose, "verbose", "v", false, "Verbose output") rootCmd.PersistentFlags().DurationVar(&cfg.Timeout, "timeout", 300*time.Second, "Timeout") + rootCmd.PersistentFlags().StringVar(&cfg.Registry, "registry", "localhost:5001", "Default registry for local builds") + + if err := viper.BindPFlag("registry", rootCmd.PersistentFlags().Lookup("registry")); err != nil { + log.Fatalf("Failed to bind pflag: %v", err) + } + installCfg := &cli.InstallCfg{ Config: cfg, } @@ -421,6 +429,24 @@ Examples: os.Exit(1) } + finalCfg, err := config.Get() + if err != nil { + fmt.Fprintf(os.Stderr, "Error loading final config: %v\n", err) + os.Exit(1) + } + + cfg = finalCfg + if buildCfg != nil { + buildCfg.Registry = cfg.Registry + } + if deployCfg != nil { + deployCfg.Registry = cfg.Registry + } + if runCfg != nil { + runCfg.Registry = cfg.Registry + } + + if err := rootCmd.ExecuteContext(ctx); err != nil { fmt.Fprintf(os.Stderr, "Error: %v\n", err) diff --git a/go/cli/internal/cli/agent/build.go b/go/cli/internal/cli/agent/build.go index 6da7474c8..fb8c3f199 100644 --- a/go/cli/internal/cli/agent/build.go +++ b/go/cli/internal/cli/agent/build.go @@ -18,6 +18,7 @@ type BuildCfg struct { Platform string Config *config.Config SkipMCPServers bool + Registry string } // BuildCmd builds a Docker image for an agent project @@ -96,7 +97,7 @@ func constructImageName(cfg *BuildCfg) string { } // Construct full image name using common utility - return commonimage.ConstructImageName(cfg.Image, agentName) + return commonimage.ConstructImageName(cfg.Image, agentName, cfg.Registry) } // getAgentNameFromManifest attempts to load the agent name from kagent.yaml @@ -202,5 +203,5 @@ func constructMcpServerImageName(cfg *BuildCfg, serverName string) string { if agentName == "" { agentName = filepath.Base(cfg.ProjectDir) } - return commonimage.ConstructMCPServerImageName(agentName, serverName) + return commonimage.ConstructMCPServerImageName(agentName, serverName, cfg.Registry) } diff --git a/go/cli/internal/cli/agent/deploy.go b/go/cli/internal/cli/agent/deploy.go index 87b783688..f8373cf67 100644 --- a/go/cli/internal/cli/agent/deploy.go +++ b/go/cli/internal/cli/agent/deploy.go @@ -65,6 +65,8 @@ type DeployCfg struct { // DryRun when true, outputs YAML manifests without actually creating resources DryRun bool + + Registry string } // DeployCmd deploys an agent to Kubernetes @@ -645,7 +647,7 @@ func createOrUpdateSecret(ctx context.Context, k8sClient client.Client, secret * // createAgentCRD creates or updates the Agent CRD func createAgentCRD(ctx context.Context, k8sClient client.Client, cfg *DeployCfg, manifest *common.AgentManifest, envData *envFileData, verbose bool) error { - imageName := determineImageName(cfg.Image, manifest.Name) + imageName := determineImageName(cfg.Image, manifest.Name, cfg.Config.Registry) agent := buildAgentCRD(cfg.Config.Namespace, manifest, imageName, envData) // In dry-run mode, just output the YAML @@ -658,8 +660,8 @@ func createAgentCRD(ctx context.Context, k8sClient client.Client, cfg *DeployCfg } // determineImageName returns the image name to use, either from config or default -func determineImageName(configImage, agentName string) string { - return commonimage.ConstructImageName(configImage, agentName) +func determineImageName(configImage, agentName, registry string) string { + return commonimage.ConstructImageName(configImage, agentName, registry) } // buildAgentCRD constructs an Agent CRD object diff --git a/go/cli/internal/cli/agent/run.go b/go/cli/internal/cli/agent/run.go index f07bd9877..988bf65a7 100644 --- a/go/cli/internal/cli/agent/run.go +++ b/go/cli/internal/cli/agent/run.go @@ -19,7 +19,8 @@ import ( type RunCfg struct { ProjectDir string Config *config.Config - Build bool + Build bool + Registry string } // RunCmd starts docker-compose in the background and launches a chat session with the local agent diff --git a/go/cli/internal/common/image/image.go b/go/cli/internal/common/image/image.go index 0240d6034..e0ae12d5b 100644 --- a/go/cli/internal/common/image/image.go +++ b/go/cli/internal/common/image/image.go @@ -10,17 +10,25 @@ const ( // ConstructImageName constructs a Docker image name with default registry and tag. // If configImage is provided (non-empty), it is returned as-is. // Otherwise, constructs: DefaultRegistry/imageName:DefaultTag (e.g., "localhost:5001/my-agent:latest") -func ConstructImageName(configImage, imageName string) string { - if configImage != "" { - return configImage - } - return fmt.Sprintf("%s/%s:%s", DefaultRegistry, imageName, DefaultTag) +func ConstructImageName(configImage, imageName, registry string) string { + if configImage != "" { + return configImage + } + + if registry == "" { + registry = DefaultRegistry + } + + return fmt.Sprintf("%s/%s:%s", registry, imageName, DefaultTag) } // ConstructMCPServerImageName constructs a Docker image name for an MCP server. // The image name follows the pattern: DefaultRegistry/agentName-serverName:DefaultTag // (e.g., "localhost:5001/my-agent-github-server:latest") -func ConstructMCPServerImageName(agentName, serverName string) string { - imageName := fmt.Sprintf("%s-%s", agentName, serverName) - return fmt.Sprintf("%s/%s:%s", DefaultRegistry, imageName, DefaultTag) +func ConstructMCPServerImageName(agentName, serverName, registry string) string { + if registry == "" { + registry = DefaultRegistry + } + imageName := fmt.Sprintf("%s-%s", agentName, serverName) + return fmt.Sprintf("%s/%s:%s", registry, imageName, DefaultTag) } diff --git a/go/cli/internal/config/config.go b/go/cli/internal/config/config.go index 46a385063..7cc456f4f 100644 --- a/go/cli/internal/config/config.go +++ b/go/cli/internal/config/config.go @@ -18,6 +18,7 @@ type Config struct { OutputFormat string `mapstructure:"output_format"` Verbose bool `mapstructure:"verbose"` Timeout time.Duration `mapstructure:"timeout"` + Registry string `mapstructure:"registry"` } func (c *Config) Client() *kagentclient.ClientSet { @@ -47,6 +48,10 @@ func Init() error { viper.SetDefault("output_format", "table") viper.SetDefault("namespace", "kagent") viper.SetDefault("timeout", 300*time.Second) + viper.SetDefault("registry", "localhost:5001") + if err := viper.BindEnv("registry", "KAGENT_REGISTRY"); err != nil { + return fmt.Errorf("error binding KAGENT_REGISTRY: %w", err) + } viper.MustBindEnv("USER_ID") if err := viper.ReadInConfig(); err != nil {