Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .infer/agents.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
---
agents:
- name: mock-agent
url: http://localhost:8081
Expand Down
1 change: 1 addition & 0 deletions .infer/config.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
---
container_runtime:
type: docker
gateway:
Expand Down
45 changes: 35 additions & 10 deletions cmd/agents.go
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,11 @@ func addAgent(cmd *cobra.Command, name, url, artifactsURL, oci string, run bool,
Environment: environment,
}

if err := config.AddAgent(path, agent); err != nil {
cfg, err := config.LoadAgents(path)
if err != nil {
return err
}
if err := cfg.CreateEntry(agent); err != nil {
return err
}

Expand Down Expand Up @@ -320,7 +324,11 @@ func updateAgent(cmd *cobra.Command, name, url, artifactsURL, oci string, run bo
return err
}

existing, err := config.GetAgent(path, name)
cfg, err := config.LoadAgents(path)
if err != nil {
return err
}
existing, err := cfg.ReadEntry(name)
if err != nil {
return err
}
Expand Down Expand Up @@ -349,7 +357,7 @@ func updateAgent(cmd *cobra.Command, name, url, artifactsURL, oci string, run bo
return fmt.Errorf("--model is required when --run is enabled. Specify a model in the format provider/model (e.g., openai/gpt-5, anthropic/claude-4-5-sonnet)")
}

if err := config.UpdateAgent(path, agent); err != nil {
if err := cfg.UpdateEntry(agent); err != nil {
return err
}

Expand Down Expand Up @@ -380,7 +388,11 @@ func removeAgent(cmd *cobra.Command, name string) error {
return err
}

if err := config.RemoveAgent(path, name); err != nil {
cfg, err := config.LoadAgents(path)
if err != nil {
return err
}
if err := cfg.DeleteEntry(name); err != nil {
return err
}

Expand All @@ -394,10 +406,11 @@ func listAgents(cmd *cobra.Command, args []string) error {
return err
}

localAgents, err := config.ListAgents(path)
cfg, err := config.LoadAgents(path)
if err != nil {
return err
}
localAgents := cfg.ListEntries()

externalAgents := extractExternalAgents(Cfg)

Expand Down Expand Up @@ -490,7 +503,11 @@ func showAgent(cmd *cobra.Command, name string) error {
return err
}

agent, err := config.GetAgent(path, name)
cfg, err := config.LoadAgents(path)
if err != nil {
return err
}
agent, err := cfg.ReadEntry(name)
if err != nil {
return err
}
Expand Down Expand Up @@ -579,13 +596,17 @@ func enableAgent(cmd *cobra.Command, name string) error {
return err
}

agent, err := config.GetAgent(path, name)
cfg, err := config.LoadAgents(path)
if err != nil {
return fmt.Errorf("failed to load agents: %w", err)
}
agent, err := cfg.ReadEntry(name)
if err != nil {
return fmt.Errorf("failed to find agent: %w", err)
}

agent.Enabled = true
if err := config.UpdateAgent(path, *agent); err != nil {
if err := cfg.UpdateEntry(*agent); err != nil {
return fmt.Errorf("failed to enable agent: %w", err)
}

Expand All @@ -602,13 +623,17 @@ func disableAgent(cmd *cobra.Command, name string) error {
return err
}

agent, err := config.GetAgent(path, name)
cfg, err := config.LoadAgents(path)
if err != nil {
return fmt.Errorf("failed to load agents: %w", err)
}
agent, err := cfg.ReadEntry(name)
if err != nil {
return fmt.Errorf("failed to find agent: %w", err)
}

agent.Enabled = false
if err := config.UpdateAgent(path, *agent); err != nil {
if err := cfg.UpdateEntry(*agent); err != nil {
return fmt.Errorf("failed to disable agent: %w", err)
}

Expand Down
7 changes: 2 additions & 5 deletions cmd/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
sdk "github.com/inference-gateway/sdk"

config "github.com/inference-gateway/cli/config"
configutils "github.com/inference-gateway/cli/config/utils"
container "github.com/inference-gateway/cli/internal/container"
formatting "github.com/inference-gateway/cli/internal/formatting"
logger "github.com/inference-gateway/cli/internal/logger"
Expand Down Expand Up @@ -108,11 +109,7 @@ For complete project initialization, use 'infer init' instead.`,
}
}

if err := os.MkdirAll(filepath.Dir(configPath), 0755); err != nil {
return fmt.Errorf("failed to create config directory: %w", err)
}

if err := writeConfigAsYAMLWithIndent(configPath, 2); err != nil {
if err := configutils.SaveYAML(configPath, "config", config.DefaultConfig()); err != nil {
return fmt.Errorf("failed to create config file: %w", err)
}

Expand Down
49 changes: 21 additions & 28 deletions cmd/init.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
package cmd

import (
"bytes"
"fmt"
"os"
"path/filepath"

cobra "github.com/spf13/cobra"
yaml "gopkg.in/yaml.v3"

config "github.com/inference-gateway/cli/config"
utils "github.com/inference-gateway/cli/config/utils"
icons "github.com/inference-gateway/cli/internal/ui/styles/icons"
)

Expand Down Expand Up @@ -40,7 +39,7 @@ func initializeProject(cmd *cobra.Command) error { //nolint:funlen
userspace, _ := cmd.Flags().GetBool("userspace")
skipMigrations, _ := cmd.Flags().GetBool("skip-migrations")

var configPath, gitignorePath, scmShortcutsPath, gitShortcutsPath, mcpShortcutsPath, shellsShortcutsPath, exportShortcutsPath, a2aShortcutsPath, mcpPath, keybindingsPath, promptsPath, channelsPath string
var configPath, gitignorePath, scmShortcutsPath, gitShortcutsPath, mcpShortcutsPath, shellsShortcutsPath, exportShortcutsPath, a2aShortcutsPath, mcpPath, keybindingsPath, promptsPath, channelsPath, agentsPath string

if userspace {
homeDir, err := os.UserHomeDir()
Expand All @@ -59,6 +58,7 @@ func initializeProject(cmd *cobra.Command) error { //nolint:funlen
keybindingsPath = filepath.Join(homeDir, config.ConfigDirName, config.KeybindingsFileName)
promptsPath = filepath.Join(homeDir, config.ConfigDirName, config.PromptsFileName)
channelsPath = filepath.Join(homeDir, config.ConfigDirName, config.ChannelsFileName)
agentsPath = filepath.Join(homeDir, config.ConfigDirName, config.AgentsFileName)
} else {
configPath = config.DefaultConfigPath
gitignorePath = filepath.Join(config.ConfigDirName, config.GitignoreFileName)
Expand All @@ -72,15 +72,16 @@ func initializeProject(cmd *cobra.Command) error { //nolint:funlen
keybindingsPath = config.DefaultKeybindingsPath
promptsPath = config.DefaultPromptsPath
channelsPath = config.DefaultChannelsPath
agentsPath = config.DefaultAgentsPath
}

if !overwrite {
if err := validateFilesNotExist(configPath, gitignorePath, scmShortcutsPath, gitShortcutsPath, mcpShortcutsPath, shellsShortcutsPath, exportShortcutsPath, a2aShortcutsPath, mcpPath, keybindingsPath, promptsPath, channelsPath); err != nil {
if err := validateFilesNotExist(configPath, gitignorePath, scmShortcutsPath, gitShortcutsPath, mcpShortcutsPath, shellsShortcutsPath, exportShortcutsPath, a2aShortcutsPath, mcpPath, keybindingsPath, promptsPath, channelsPath, agentsPath); err != nil {
return err
}
}

if err := writeConfigAsYAMLWithIndent(configPath, 2); err != nil {
if err := utils.SaveYAML(configPath, "config", config.DefaultConfig()); err != nil {
return fmt.Errorf("failed to create config file: %w", err)
}

Expand Down Expand Up @@ -139,6 +140,10 @@ tmp/
return fmt.Errorf("failed to create channels config file: %w", err)
}

if err := createAgentsConfigFile(agentsPath); err != nil {
return fmt.Errorf("failed to create agents config file: %w", err)
}

var scopeDesc string
if userspace {
scopeDesc = "userspace"
Expand All @@ -159,6 +164,7 @@ tmp/
fmt.Printf(" Created: %s\n", keybindingsPath)
fmt.Printf(" Created: %s\n", promptsPath)
fmt.Printf(" Created: %s\n", channelsPath)
fmt.Printf(" Created: %s\n", agentsPath)
if migrated {
fmt.Printf("\n%s Migrated legacy `channels:` block from config.yaml into %s.\n", icons.CheckMarkStyle.Render(icons.CheckMark), channelsPath)
fmt.Printf(" You can now remove the `channels:` block from %s.\n", configPath)
Expand All @@ -184,29 +190,6 @@ tmp/
return nil
}

// writeConfigAsYAMLWithIndent writes the default configuration to a YAML file with specified indentation
func writeConfigAsYAMLWithIndent(filename string, indent int) error {
defaultConfig := config.DefaultConfig()

if err := os.MkdirAll(filepath.Dir(filename), 0755); err != nil {
return fmt.Errorf("failed to create directory: %w", err)
}

var buf bytes.Buffer
yamlEncoder := yaml.NewEncoder(&buf)
yamlEncoder.SetIndent(indent)

if err := yamlEncoder.Encode(defaultConfig); err != nil {
return fmt.Errorf("failed to marshal config to YAML: %w", err)
}

if err := yamlEncoder.Close(); err != nil {
return fmt.Errorf("failed to close YAML encoder: %w", err)
}

return os.WriteFile(filename, buf.Bytes(), 0644)
}

// checkFileExists checks if a file exists and returns an error if it does
func checkFileExists(path, description string) error {
if _, err := os.Stat(path); err == nil {
Expand Down Expand Up @@ -466,6 +449,16 @@ func createChannelsConfigFile(path string) (bool, error) {
return migrated, nil
}

// createAgentsConfigFile writes a fresh agents.yaml seeded from the in-code
// defaults so users can manage A2A agents via `infer agents` commands.
func createAgentsConfigFile(path string) error {
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
return fmt.Errorf("failed to create config directory: %w", err)
}

return config.SaveAgents(path, config.DefaultAgentsConfig())
}

// createMCPConfigFile creates the MCP configuration YAML file
func createMCPConfigFile(path string) error {
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
Expand Down
20 changes: 11 additions & 9 deletions cmd/init_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import (
"testing"

cobra "github.com/spf13/cobra"

config "github.com/inference-gateway/cli/config"
configutils "github.com/inference-gateway/cli/config/utils"
)

func TestInitializeProject(t *testing.T) {
Expand Down Expand Up @@ -96,7 +99,7 @@ func TestInitializeProject(t *testing.T) {
}
}

func TestWriteConfigAsYAMLWithIndent(t *testing.T) {
func TestInitWritesConfigYAMLWithDocMarker(t *testing.T) {
tmpDir, err := os.MkdirTemp("", "infer-config-test-*")
if err != nil {
t.Fatalf("failed to create temp dir: %v", err)
Expand All @@ -105,23 +108,22 @@ func TestWriteConfigAsYAMLWithIndent(t *testing.T) {

configPath := tmpDir + "/.infer/config.yaml"

err = writeConfigAsYAMLWithIndent(configPath, 2)
if err != nil {
t.Errorf("writeConfigAsYAMLWithIndent() error = %v", err)
return
if err := configutils.SaveYAML(configPath, "config", config.DefaultConfig()); err != nil {
t.Fatalf("SaveYAML() error = %v", err)
}

if _, err := os.Stat(configPath); os.IsNotExist(err) {
t.Errorf("expected config file to be created")
return
t.Fatal("expected config file to be created")
}

content, err := os.ReadFile(configPath)
if err != nil {
t.Errorf("failed to read config file: %v", err)
return
t.Fatalf("failed to read config file: %v", err)
}

if !strings.HasPrefix(string(content), "---\n") {
t.Errorf("config file should start with `---\\n`, got %q", string(content[:min(8, len(content))]))
}
if !strings.Contains(string(content), "gateway:") {
t.Errorf("config file does not contain expected gateway section")
}
Expand Down
Loading