Feat/add tool enable or disable configuration (#1071)
* Add tools enable or diable config
This commit is contained in:
parent
10ad9e83f9
commit
6f5930624b
8 changed files with 367 additions and 123 deletions
|
|
@ -230,19 +230,25 @@ func setupCronTool(
|
||||||
// Create cron service
|
// Create cron service
|
||||||
cronService := cron.NewCronService(cronStorePath, nil)
|
cronService := cron.NewCronService(cronStorePath, nil)
|
||||||
|
|
||||||
// Create and register CronTool
|
// Create and register CronTool if enabled
|
||||||
cronTool, err := tools.NewCronTool(cronService, agentLoop, msgBus, workspace, restrict, execTimeout, cfg)
|
var cronTool *tools.CronTool
|
||||||
|
if cfg.Tools.IsToolEnabled("cron") {
|
||||||
|
var err error
|
||||||
|
cronTool, err = tools.NewCronTool(cronService, agentLoop, msgBus, workspace, restrict, execTimeout, cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Critical error during CronTool initialization: %v", err)
|
log.Fatalf("Critical error during CronTool initialization: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
agentLoop.RegisterTool(cronTool)
|
agentLoop.RegisterTool(cronTool)
|
||||||
|
}
|
||||||
|
|
||||||
// Set the onJob handler
|
// Set onJob handler
|
||||||
|
if cronTool != nil {
|
||||||
cronService.SetOnJob(func(job *cron.CronJob) (string, error) {
|
cronService.SetOnJob(func(job *cron.CronJob) (string, error) {
|
||||||
result := cronTool.ExecuteJob(context.Background(), job)
|
result := cronTool.ExecuteJob(context.Background(), job)
|
||||||
return result, nil
|
return result, nil
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return cronService
|
return cronService
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -232,24 +232,41 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"tools": {
|
"tools": {
|
||||||
|
"allow_read_paths": null,
|
||||||
|
"allow_write_paths": null,
|
||||||
"web": {
|
"web": {
|
||||||
|
"enabled": true,
|
||||||
"brave": {
|
"brave": {
|
||||||
"enabled": false,
|
"enabled": false,
|
||||||
"api_key": "YOUR_BRAVE_API_KEY",
|
"api_key": "YOUR_BRAVE_API_KEY",
|
||||||
"max_results": 5
|
"max_results": 5
|
||||||
},
|
},
|
||||||
|
"tavily": {
|
||||||
|
"enabled": false,
|
||||||
|
"api_key": "",
|
||||||
|
"base_url": "",
|
||||||
|
"max_results": 0
|
||||||
|
},
|
||||||
"duckduckgo": {
|
"duckduckgo": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"max_results": 5
|
"max_results": 5
|
||||||
},
|
},
|
||||||
"perplexity": {
|
"perplexity": {
|
||||||
"enabled": false,
|
"enabled": false,
|
||||||
"api_key": "pplx-xxx",
|
"api_key": "",
|
||||||
"max_results": 5
|
"max_results": 5
|
||||||
},
|
},
|
||||||
"proxy": ""
|
"glm_search": {
|
||||||
|
"enabled": false,
|
||||||
|
"api_key": "",
|
||||||
|
"base_url": "https://open.bigmodel.cn/api/paas/v4/web_search",
|
||||||
|
"search_engine": "search_std",
|
||||||
|
"max_results": 5
|
||||||
|
},
|
||||||
|
"fetch_limit_bytes": 10485760
|
||||||
},
|
},
|
||||||
"cron": {
|
"cron": {
|
||||||
|
"enabled": true,
|
||||||
"exec_timeout_minutes": 5
|
"exec_timeout_minutes": 5
|
||||||
},
|
},
|
||||||
"mcp": {
|
"mcp": {
|
||||||
|
|
@ -318,19 +335,75 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"exec": {
|
"exec": {
|
||||||
"enable_deny_patterns": false,
|
"enabled": true,
|
||||||
"custom_deny_patterns": []
|
"enable_deny_patterns": true,
|
||||||
|
"custom_deny_patterns": null,
|
||||||
|
"custom_allow_patterns": null
|
||||||
},
|
},
|
||||||
"skills": {
|
"skills": {
|
||||||
|
"enabled": true,
|
||||||
"registries": {
|
"registries": {
|
||||||
"clawhub": {
|
"clawhub": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"base_url": "https://clawhub.ai",
|
"base_url": "https://clawhub.ai",
|
||||||
"search_path": "/api/v1/search",
|
"auth_token": "",
|
||||||
"skills_path": "/api/v1/skills",
|
"search_path": "",
|
||||||
"download_path": "/api/v1/download"
|
"skills_path": "",
|
||||||
|
"download_path": "",
|
||||||
|
"timeout": 0,
|
||||||
|
"max_zip_size": 0,
|
||||||
|
"max_response_size": 0
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"max_concurrent_searches": 2,
|
||||||
|
"search_cache": {
|
||||||
|
"max_size": 50,
|
||||||
|
"ttl_seconds": 300
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"media_cleanup": {
|
||||||
|
"enabled": true,
|
||||||
|
"max_age_minutes": 30,
|
||||||
|
"interval_minutes": 5
|
||||||
|
},
|
||||||
|
"append_file": {
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
"edit_file": {
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
"find_skills": {
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
"i2c": {
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
"install_skill": {
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
"list_dir": {
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
"message": {
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
"read_file": {
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
"spawn": {
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
"spi": {
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
"subagent": {
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
"web_fetch": {
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
"write_file": {
|
||||||
|
"enabled": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"heartbeat": {
|
"heartbeat": {
|
||||||
|
|
|
||||||
|
|
@ -60,17 +60,30 @@ func NewAgentInstance(
|
||||||
allowWritePaths := compilePatterns(cfg.Tools.AllowWritePaths)
|
allowWritePaths := compilePatterns(cfg.Tools.AllowWritePaths)
|
||||||
|
|
||||||
toolsRegistry := tools.NewToolRegistry()
|
toolsRegistry := tools.NewToolRegistry()
|
||||||
|
|
||||||
|
if cfg.Tools.IsToolEnabled("read_file") {
|
||||||
toolsRegistry.Register(tools.NewReadFileTool(workspace, readRestrict, allowReadPaths))
|
toolsRegistry.Register(tools.NewReadFileTool(workspace, readRestrict, allowReadPaths))
|
||||||
|
}
|
||||||
|
if cfg.Tools.IsToolEnabled("write_file") {
|
||||||
toolsRegistry.Register(tools.NewWriteFileTool(workspace, restrict, allowWritePaths))
|
toolsRegistry.Register(tools.NewWriteFileTool(workspace, restrict, allowWritePaths))
|
||||||
|
}
|
||||||
|
if cfg.Tools.IsToolEnabled("list_dir") {
|
||||||
toolsRegistry.Register(tools.NewListDirTool(workspace, readRestrict, allowReadPaths))
|
toolsRegistry.Register(tools.NewListDirTool(workspace, readRestrict, allowReadPaths))
|
||||||
|
}
|
||||||
|
if cfg.Tools.IsToolEnabled("exec") {
|
||||||
execTool, err := tools.NewExecToolWithConfig(workspace, restrict, cfg)
|
execTool, err := tools.NewExecToolWithConfig(workspace, restrict, cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Critical error: unable to initialize exec tool: %v", err)
|
log.Fatalf("Critical error: unable to initialize exec tool: %v", err)
|
||||||
}
|
}
|
||||||
toolsRegistry.Register(execTool)
|
toolsRegistry.Register(execTool)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.Tools.IsToolEnabled("edit_file") {
|
||||||
toolsRegistry.Register(tools.NewEditFileTool(workspace, restrict, allowWritePaths))
|
toolsRegistry.Register(tools.NewEditFileTool(workspace, restrict, allowWritePaths))
|
||||||
|
}
|
||||||
|
if cfg.Tools.IsToolEnabled("append_file") {
|
||||||
toolsRegistry.Register(tools.NewAppendFileTool(workspace, restrict, allowWritePaths))
|
toolsRegistry.Register(tools.NewAppendFileTool(workspace, restrict, allowWritePaths))
|
||||||
|
}
|
||||||
|
|
||||||
sessionsDir := filepath.Join(workspace, "sessions")
|
sessionsDir := filepath.Join(workspace, "sessions")
|
||||||
sessionsManager := session.NewSessionManager(sessionsDir)
|
sessionsManager := session.NewSessionManager(sessionsDir)
|
||||||
|
|
|
||||||
|
|
@ -108,6 +108,7 @@ func registerSharedTools(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Web tools
|
// Web tools
|
||||||
|
if cfg.Tools.IsToolEnabled("web") {
|
||||||
searchTool, err := tools.NewWebSearchTool(tools.WebSearchToolOptions{
|
searchTool, err := tools.NewWebSearchTool(tools.WebSearchToolOptions{
|
||||||
BraveAPIKey: cfg.Tools.Web.Brave.APIKey,
|
BraveAPIKey: cfg.Tools.Web.Brave.APIKey,
|
||||||
BraveMaxResults: cfg.Tools.Web.Brave.MaxResults,
|
BraveMaxResults: cfg.Tools.Web.Brave.MaxResults,
|
||||||
|
|
@ -133,18 +134,26 @@ func registerSharedTools(
|
||||||
} else if searchTool != nil {
|
} else if searchTool != nil {
|
||||||
agent.Tools.Register(searchTool)
|
agent.Tools.Register(searchTool)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if cfg.Tools.IsToolEnabled("web_fetch") {
|
||||||
fetchTool, err := tools.NewWebFetchToolWithProxy(50000, cfg.Tools.Web.Proxy, cfg.Tools.Web.FetchLimitBytes)
|
fetchTool, err := tools.NewWebFetchToolWithProxy(50000, cfg.Tools.Web.Proxy, cfg.Tools.Web.FetchLimitBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.ErrorCF("agent", "Failed to create web fetch tool", map[string]any{"error": err.Error()})
|
logger.ErrorCF("agent", "Failed to create web fetch tool", map[string]any{"error": err.Error()})
|
||||||
} else {
|
} else {
|
||||||
agent.Tools.Register(fetchTool)
|
agent.Tools.Register(fetchTool)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Hardware tools (I2C, SPI) - Linux only, returns error on other platforms
|
// Hardware tools (I2C, SPI) - Linux only, returns error on other platforms
|
||||||
|
if cfg.Tools.IsToolEnabled("i2c") {
|
||||||
agent.Tools.Register(tools.NewI2CTool())
|
agent.Tools.Register(tools.NewI2CTool())
|
||||||
|
}
|
||||||
|
if cfg.Tools.IsToolEnabled("spi") {
|
||||||
agent.Tools.Register(tools.NewSPITool())
|
agent.Tools.Register(tools.NewSPITool())
|
||||||
|
}
|
||||||
|
|
||||||
// Message tool
|
// Message tool
|
||||||
|
if cfg.Tools.IsToolEnabled("message") {
|
||||||
messageTool := tools.NewMessageTool()
|
messageTool := tools.NewMessageTool()
|
||||||
messageTool.SetSendCallback(func(channel, chatID, content string) error {
|
messageTool.SetSendCallback(func(channel, chatID, content string) error {
|
||||||
pubCtx, pubCancel := context.WithTimeout(context.Background(), 5*time.Second)
|
pubCtx, pubCancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
|
|
@ -156,20 +165,33 @@ func registerSharedTools(
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
agent.Tools.Register(messageTool)
|
agent.Tools.Register(messageTool)
|
||||||
|
}
|
||||||
|
|
||||||
// Skill discovery and installation tools
|
// Skill discovery and installation tools
|
||||||
|
find_skills_enable := cfg.Tools.IsToolEnabled("find_skills")
|
||||||
|
install_skills_enable := cfg.Tools.IsToolEnabled("install_skill")
|
||||||
|
if find_skills_enable || install_skills_enable {
|
||||||
registryMgr := skills.NewRegistryManagerFromConfig(skills.RegistryConfig{
|
registryMgr := skills.NewRegistryManagerFromConfig(skills.RegistryConfig{
|
||||||
MaxConcurrentSearches: cfg.Tools.Skills.MaxConcurrentSearches,
|
MaxConcurrentSearches: cfg.Tools.Skills.MaxConcurrentSearches,
|
||||||
ClawHub: skills.ClawHubConfig(cfg.Tools.Skills.Registries.ClawHub),
|
ClawHub: skills.ClawHubConfig(cfg.Tools.Skills.Registries.ClawHub),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if find_skills_enable {
|
||||||
searchCache := skills.NewSearchCache(
|
searchCache := skills.NewSearchCache(
|
||||||
cfg.Tools.Skills.SearchCache.MaxSize,
|
cfg.Tools.Skills.SearchCache.MaxSize,
|
||||||
time.Duration(cfg.Tools.Skills.SearchCache.TTLSeconds)*time.Second,
|
time.Duration(cfg.Tools.Skills.SearchCache.TTLSeconds)*time.Second,
|
||||||
)
|
)
|
||||||
agent.Tools.Register(tools.NewFindSkillsTool(registryMgr, searchCache))
|
agent.Tools.Register(tools.NewFindSkillsTool(registryMgr, searchCache))
|
||||||
|
}
|
||||||
|
|
||||||
|
if install_skills_enable {
|
||||||
agent.Tools.Register(tools.NewInstallSkillTool(registryMgr, agent.Workspace))
|
agent.Tools.Register(tools.NewInstallSkillTool(registryMgr, agent.Workspace))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Spawn tool with allowlist checker
|
// Spawn tool with allowlist checker
|
||||||
|
if cfg.Tools.IsToolEnabled("spawn") {
|
||||||
|
if cfg.Tools.IsToolEnabled("subagent") {
|
||||||
subagentManager := tools.NewSubagentManager(provider, agent.Model, agent.Workspace, msgBus)
|
subagentManager := tools.NewSubagentManager(provider, agent.Model, agent.Workspace, msgBus)
|
||||||
subagentManager.SetLLMOptions(agent.MaxTokens, agent.Temperature)
|
subagentManager.SetLLMOptions(agent.MaxTokens, agent.Temperature)
|
||||||
spawnTool := tools.NewSpawnTool(subagentManager)
|
spawnTool := tools.NewSpawnTool(subagentManager)
|
||||||
|
|
@ -178,6 +200,10 @@ func registerSharedTools(
|
||||||
return registry.CanSpawnSubagent(currentAgentID, targetAgentID)
|
return registry.CanSpawnSubagent(currentAgentID, targetAgentID)
|
||||||
})
|
})
|
||||||
agent.Tools.Register(spawnTool)
|
agent.Tools.Register(spawnTool)
|
||||||
|
} else {
|
||||||
|
logger.WarnCF("agent", "spawn tool requires subagent to be enabled", nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -185,7 +211,7 @@ func (al *AgentLoop) Run(ctx context.Context) error {
|
||||||
al.running.Store(true)
|
al.running.Store(true)
|
||||||
|
|
||||||
// Initialize MCP servers for all agents
|
// Initialize MCP servers for all agents
|
||||||
if al.cfg.Tools.MCP.Enabled {
|
if al.cfg.Tools.IsToolEnabled("mcp") {
|
||||||
mcpManager := mcp.NewManager()
|
mcpManager := mcp.NewManager()
|
||||||
// Ensure MCP connections are cleaned up on exit, regardless of initialization success
|
// Ensure MCP connections are cleaned up on exit, regardless of initialization success
|
||||||
// This fixes resource leak when LoadFromMCPConfig partially succeeds then fails
|
// This fixes resource leak when LoadFromMCPConfig partially succeeds then fails
|
||||||
|
|
@ -227,6 +253,7 @@ func (al *AgentLoop) Run(ctx context.Context) error {
|
||||||
if !ok {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
mcpTool := tools.NewMCPTool(mcpManager, serverName, tool)
|
mcpTool := tools.NewMCPTool(mcpManager, serverName, tool)
|
||||||
agent.Tools.Register(mcpTool)
|
agent.Tools.Register(mcpTool)
|
||||||
totalRegistrations++
|
totalRegistrations++
|
||||||
|
|
|
||||||
|
|
@ -227,16 +227,11 @@ func TestAgentLoop_GetStartupInfo(t *testing.T) {
|
||||||
}
|
}
|
||||||
defer os.RemoveAll(tmpDir)
|
defer os.RemoveAll(tmpDir)
|
||||||
|
|
||||||
cfg := &config.Config{
|
cfg := config.DefaultConfig()
|
||||||
Agents: config.AgentsConfig{
|
cfg.Agents.Defaults.Workspace = tmpDir
|
||||||
Defaults: config.AgentDefaults{
|
cfg.Agents.Defaults.Model = "test-model"
|
||||||
Workspace: tmpDir,
|
cfg.Agents.Defaults.MaxTokens = 4096
|
||||||
Model: "test-model",
|
cfg.Agents.Defaults.MaxToolIterations = 10
|
||||||
MaxTokens: 4096,
|
|
||||||
MaxToolIterations: 10,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
msgBus := bus.NewMessageBus()
|
msgBus := bus.NewMessageBus()
|
||||||
provider := &mockProvider{}
|
provider := &mockProvider{}
|
||||||
|
|
|
||||||
|
|
@ -526,6 +526,10 @@ type GatewayConfig struct {
|
||||||
Port int `json:"port" env:"PICOCLAW_GATEWAY_PORT"`
|
Port int `json:"port" env:"PICOCLAW_GATEWAY_PORT"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ToolConfig struct {
|
||||||
|
Enabled bool `json:"enabled" env:"ENABLED"`
|
||||||
|
}
|
||||||
|
|
||||||
type BraveConfig struct {
|
type BraveConfig struct {
|
||||||
Enabled bool `json:"enabled" env:"PICOCLAW_TOOLS_WEB_BRAVE_ENABLED"`
|
Enabled bool `json:"enabled" env:"PICOCLAW_TOOLS_WEB_BRAVE_ENABLED"`
|
||||||
APIKey string `json:"api_key" env:"PICOCLAW_TOOLS_WEB_BRAVE_API_KEY"`
|
APIKey string `json:"api_key" env:"PICOCLAW_TOOLS_WEB_BRAVE_API_KEY"`
|
||||||
|
|
@ -561,11 +565,12 @@ type GLMSearchConfig struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type WebToolsConfig struct {
|
type WebToolsConfig struct {
|
||||||
Brave BraveConfig `json:"brave"`
|
ToolConfig ` envPrefix:"PICOCLAW_TOOLS_WEB_"`
|
||||||
Tavily TavilyConfig `json:"tavily"`
|
Brave BraveConfig ` json:"brave"`
|
||||||
DuckDuckGo DuckDuckGoConfig `json:"duckduckgo"`
|
Tavily TavilyConfig ` json:"tavily"`
|
||||||
Perplexity PerplexityConfig `json:"perplexity"`
|
DuckDuckGo DuckDuckGoConfig ` json:"duckduckgo"`
|
||||||
GLMSearch GLMSearchConfig `json:"glm_search"`
|
Perplexity PerplexityConfig ` json:"perplexity"`
|
||||||
|
GLMSearch GLMSearchConfig ` json:"glm_search"`
|
||||||
// Proxy is an optional proxy URL for web tools (http/https/socks5/socks5h).
|
// Proxy is an optional proxy URL for web tools (http/https/socks5/socks5h).
|
||||||
// For authenticated proxies, prefer HTTP_PROXY/HTTPS_PROXY env vars instead of embedding credentials in config.
|
// For authenticated proxies, prefer HTTP_PROXY/HTTPS_PROXY env vars instead of embedding credentials in config.
|
||||||
Proxy string `json:"proxy,omitempty" env:"PICOCLAW_TOOLS_WEB_PROXY"`
|
Proxy string `json:"proxy,omitempty" env:"PICOCLAW_TOOLS_WEB_PROXY"`
|
||||||
|
|
@ -573,19 +578,28 @@ type WebToolsConfig struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type CronToolsConfig struct {
|
type CronToolsConfig struct {
|
||||||
ExecTimeoutMinutes int `json:"exec_timeout_minutes" env:"PICOCLAW_TOOLS_CRON_EXEC_TIMEOUT_MINUTES"` // 0 means no timeout
|
ToolConfig ` envPrefix:"PICOCLAW_TOOLS_CRON_"`
|
||||||
|
ExecTimeoutMinutes int ` env:"PICOCLAW_TOOLS_CRON_EXEC_TIMEOUT_MINUTES" json:"exec_timeout_minutes"` // 0 means no timeout
|
||||||
}
|
}
|
||||||
|
|
||||||
type ExecConfig struct {
|
type ExecConfig struct {
|
||||||
EnableDenyPatterns bool `json:"enable_deny_patterns" env:"PICOCLAW_TOOLS_EXEC_ENABLE_DENY_PATTERNS"`
|
ToolConfig ` envPrefix:"PICOCLAW_TOOLS_EXEC_"`
|
||||||
CustomDenyPatterns []string `json:"custom_deny_patterns" env:"PICOCLAW_TOOLS_EXEC_CUSTOM_DENY_PATTERNS"`
|
EnableDenyPatterns bool ` env:"PICOCLAW_TOOLS_EXEC_ENABLE_DENY_PATTERNS" json:"enable_deny_patterns"`
|
||||||
CustomAllowPatterns []string `json:"custom_allow_patterns" env:"PICOCLAW_TOOLS_EXEC_CUSTOM_ALLOW_PATTERNS"`
|
CustomDenyPatterns []string ` env:"PICOCLAW_TOOLS_EXEC_CUSTOM_DENY_PATTERNS" json:"custom_deny_patterns"`
|
||||||
|
CustomAllowPatterns []string ` env:"PICOCLAW_TOOLS_EXEC_CUSTOM_ALLOW_PATTERNS" json:"custom_allow_patterns"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SkillsToolsConfig struct {
|
||||||
|
ToolConfig ` envPrefix:"PICOCLAW_TOOLS_SKILLS_"`
|
||||||
|
Registries SkillsRegistriesConfig ` json:"registries"`
|
||||||
|
MaxConcurrentSearches int ` json:"max_concurrent_searches" env:"PICOCLAW_TOOLS_SKILLS_MAX_CONCURRENT_SEARCHES"`
|
||||||
|
SearchCache SearchCacheConfig ` json:"search_cache"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type MediaCleanupConfig struct {
|
type MediaCleanupConfig struct {
|
||||||
Enabled bool `json:"enabled" env:"PICOCLAW_MEDIA_CLEANUP_ENABLED"`
|
ToolConfig ` envPrefix:"PICOCLAW_MEDIA_CLEANUP_"`
|
||||||
MaxAge int `json:"max_age_minutes" env:"PICOCLAW_MEDIA_CLEANUP_MAX_AGE"`
|
MaxAge int ` env:"PICOCLAW_MEDIA_CLEANUP_MAX_AGE" json:"max_age_minutes"`
|
||||||
Interval int `json:"interval_minutes" env:"PICOCLAW_MEDIA_CLEANUP_INTERVAL"`
|
Interval int ` env:"PICOCLAW_MEDIA_CLEANUP_INTERVAL" json:"interval_minutes"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ToolsConfig struct {
|
type ToolsConfig struct {
|
||||||
|
|
@ -597,12 +611,19 @@ type ToolsConfig struct {
|
||||||
Skills SkillsToolsConfig `json:"skills"`
|
Skills SkillsToolsConfig `json:"skills"`
|
||||||
MediaCleanup MediaCleanupConfig `json:"media_cleanup"`
|
MediaCleanup MediaCleanupConfig `json:"media_cleanup"`
|
||||||
MCP MCPConfig `json:"mcp"`
|
MCP MCPConfig `json:"mcp"`
|
||||||
}
|
AppendFile ToolConfig `json:"append_file" envPrefix:"PICOCLAW_TOOLS_APPEND_FILE_"`
|
||||||
|
EditFile ToolConfig `json:"edit_file" envPrefix:"PICOCLAW_TOOLS_EDIT_FILE_"`
|
||||||
type SkillsToolsConfig struct {
|
FindSkills ToolConfig `json:"find_skills" envPrefix:"PICOCLAW_TOOLS_FIND_SKILLS_"`
|
||||||
Registries SkillsRegistriesConfig `json:"registries"`
|
I2C ToolConfig `json:"i2c" envPrefix:"PICOCLAW_TOOLS_I2C_"`
|
||||||
MaxConcurrentSearches int `json:"max_concurrent_searches" env:"PICOCLAW_SKILLS_MAX_CONCURRENT_SEARCHES"`
|
InstallSkill ToolConfig `json:"install_skill" envPrefix:"PICOCLAW_TOOLS_INSTALL_SKILL_"`
|
||||||
SearchCache SearchCacheConfig `json:"search_cache"`
|
ListDir ToolConfig `json:"list_dir" envPrefix:"PICOCLAW_TOOLS_LIST_DIR_"`
|
||||||
|
Message ToolConfig `json:"message" envPrefix:"PICOCLAW_TOOLS_MESSAGE_"`
|
||||||
|
ReadFile ToolConfig `json:"read_file" envPrefix:"PICOCLAW_TOOLS_READ_FILE_"`
|
||||||
|
Spawn ToolConfig `json:"spawn" envPrefix:"PICOCLAW_TOOLS_SPAWN_"`
|
||||||
|
SPI ToolConfig `json:"spi" envPrefix:"PICOCLAW_TOOLS_SPI_"`
|
||||||
|
Subagent ToolConfig `json:"subagent" envPrefix:"PICOCLAW_TOOLS_SUBAGENT_"`
|
||||||
|
WebFetch ToolConfig `json:"web_fetch" envPrefix:"PICOCLAW_TOOLS_WEB_FETCH_"`
|
||||||
|
WriteFile ToolConfig `json:"write_file" envPrefix:"PICOCLAW_TOOLS_WRITE_FILE_"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type SearchCacheConfig struct {
|
type SearchCacheConfig struct {
|
||||||
|
|
@ -648,8 +669,7 @@ type MCPServerConfig struct {
|
||||||
|
|
||||||
// MCPConfig defines configuration for all MCP servers
|
// MCPConfig defines configuration for all MCP servers
|
||||||
type MCPConfig struct {
|
type MCPConfig struct {
|
||||||
// Enabled globally enables/disables MCP integration
|
ToolConfig `envPrefix:"PICOCLAW_TOOLS_MCP_"`
|
||||||
Enabled bool `json:"enabled" env:"PICOCLAW_TOOLS_MCP_ENABLED"`
|
|
||||||
// Servers is a map of server name to server configuration
|
// Servers is a map of server name to server configuration
|
||||||
Servers map[string]MCPServerConfig `json:"servers,omitempty"`
|
Servers map[string]MCPServerConfig `json:"servers,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
@ -835,3 +855,48 @@ func (c *Config) ValidateModelList() error {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *ToolsConfig) IsToolEnabled(name string) bool {
|
||||||
|
switch name {
|
||||||
|
case "web":
|
||||||
|
return t.Web.Enabled
|
||||||
|
case "cron":
|
||||||
|
return t.Cron.Enabled
|
||||||
|
case "exec":
|
||||||
|
return t.Exec.Enabled
|
||||||
|
case "skills":
|
||||||
|
return t.Skills.Enabled
|
||||||
|
case "media_cleanup":
|
||||||
|
return t.MediaCleanup.Enabled
|
||||||
|
case "append_file":
|
||||||
|
return t.AppendFile.Enabled
|
||||||
|
case "edit_file":
|
||||||
|
return t.EditFile.Enabled
|
||||||
|
case "find_skills":
|
||||||
|
return t.FindSkills.Enabled
|
||||||
|
case "i2c":
|
||||||
|
return t.I2C.Enabled
|
||||||
|
case "install_skill":
|
||||||
|
return t.InstallSkill.Enabled
|
||||||
|
case "list_dir":
|
||||||
|
return t.ListDir.Enabled
|
||||||
|
case "message":
|
||||||
|
return t.Message.Enabled
|
||||||
|
case "read_file":
|
||||||
|
return t.ReadFile.Enabled
|
||||||
|
case "spawn":
|
||||||
|
return t.Spawn.Enabled
|
||||||
|
case "spi":
|
||||||
|
return t.SPI.Enabled
|
||||||
|
case "subagent":
|
||||||
|
return t.Subagent.Enabled
|
||||||
|
case "web_fetch":
|
||||||
|
return t.WebFetch.Enabled
|
||||||
|
case "write_file":
|
||||||
|
return t.WriteFile.Enabled
|
||||||
|
case "mcp":
|
||||||
|
return t.MCP.Enabled
|
||||||
|
default:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -336,11 +336,16 @@ func DefaultConfig() *Config {
|
||||||
},
|
},
|
||||||
Tools: ToolsConfig{
|
Tools: ToolsConfig{
|
||||||
MediaCleanup: MediaCleanupConfig{
|
MediaCleanup: MediaCleanupConfig{
|
||||||
|
ToolConfig: ToolConfig{
|
||||||
Enabled: true,
|
Enabled: true,
|
||||||
|
},
|
||||||
MaxAge: 30,
|
MaxAge: 30,
|
||||||
Interval: 5,
|
Interval: 5,
|
||||||
},
|
},
|
||||||
Web: WebToolsConfig{
|
Web: WebToolsConfig{
|
||||||
|
ToolConfig: ToolConfig{
|
||||||
|
Enabled: true,
|
||||||
|
},
|
||||||
Proxy: "",
|
Proxy: "",
|
||||||
FetchLimitBytes: 10 * 1024 * 1024, // 10MB by default
|
FetchLimitBytes: 10 * 1024 * 1024, // 10MB by default
|
||||||
Brave: BraveConfig{
|
Brave: BraveConfig{
|
||||||
|
|
@ -366,12 +371,21 @@ func DefaultConfig() *Config {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Cron: CronToolsConfig{
|
Cron: CronToolsConfig{
|
||||||
|
ToolConfig: ToolConfig{
|
||||||
|
Enabled: true,
|
||||||
|
},
|
||||||
ExecTimeoutMinutes: 5,
|
ExecTimeoutMinutes: 5,
|
||||||
},
|
},
|
||||||
Exec: ExecConfig{
|
Exec: ExecConfig{
|
||||||
|
ToolConfig: ToolConfig{
|
||||||
|
Enabled: true,
|
||||||
|
},
|
||||||
EnableDenyPatterns: true,
|
EnableDenyPatterns: true,
|
||||||
},
|
},
|
||||||
Skills: SkillsToolsConfig{
|
Skills: SkillsToolsConfig{
|
||||||
|
ToolConfig: ToolConfig{
|
||||||
|
Enabled: true,
|
||||||
|
},
|
||||||
Registries: SkillsRegistriesConfig{
|
Registries: SkillsRegistriesConfig{
|
||||||
ClawHub: ClawHubRegistryConfig{
|
ClawHub: ClawHubRegistryConfig{
|
||||||
Enabled: true,
|
Enabled: true,
|
||||||
|
|
@ -385,9 +399,50 @@ func DefaultConfig() *Config {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
MCP: MCPConfig{
|
MCP: MCPConfig{
|
||||||
|
ToolConfig: ToolConfig{
|
||||||
Enabled: false,
|
Enabled: false,
|
||||||
|
},
|
||||||
Servers: map[string]MCPServerConfig{},
|
Servers: map[string]MCPServerConfig{},
|
||||||
},
|
},
|
||||||
|
AppendFile: ToolConfig{
|
||||||
|
Enabled: true,
|
||||||
|
},
|
||||||
|
EditFile: ToolConfig{
|
||||||
|
Enabled: true,
|
||||||
|
},
|
||||||
|
FindSkills: ToolConfig{
|
||||||
|
Enabled: true,
|
||||||
|
},
|
||||||
|
I2C: ToolConfig{
|
||||||
|
Enabled: false, // Hardware tool - Linux only
|
||||||
|
},
|
||||||
|
InstallSkill: ToolConfig{
|
||||||
|
Enabled: true,
|
||||||
|
},
|
||||||
|
ListDir: ToolConfig{
|
||||||
|
Enabled: true,
|
||||||
|
},
|
||||||
|
Message: ToolConfig{
|
||||||
|
Enabled: true,
|
||||||
|
},
|
||||||
|
ReadFile: ToolConfig{
|
||||||
|
Enabled: true,
|
||||||
|
},
|
||||||
|
Spawn: ToolConfig{
|
||||||
|
Enabled: true,
|
||||||
|
},
|
||||||
|
SPI: ToolConfig{
|
||||||
|
Enabled: false, // Hardware tool - Linux only
|
||||||
|
},
|
||||||
|
Subagent: ToolConfig{
|
||||||
|
Enabled: true,
|
||||||
|
},
|
||||||
|
WebFetch: ToolConfig{
|
||||||
|
Enabled: true,
|
||||||
|
},
|
||||||
|
WriteFile: ToolConfig{
|
||||||
|
Enabled: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Heartbeat: HeartbeatConfig{
|
Heartbeat: HeartbeatConfig{
|
||||||
Enabled: true,
|
Enabled: true,
|
||||||
|
|
|
||||||
|
|
@ -194,7 +194,9 @@ func TestLoadFromMCPConfig_EmptyWorkspaceWithRelativeEnvFile(t *testing.T) {
|
||||||
mgr := NewManager()
|
mgr := NewManager()
|
||||||
|
|
||||||
mcpCfg := config.MCPConfig{
|
mcpCfg := config.MCPConfig{
|
||||||
|
ToolConfig: config.ToolConfig{
|
||||||
Enabled: true,
|
Enabled: true,
|
||||||
|
},
|
||||||
Servers: map[string]config.MCPServerConfig{
|
Servers: map[string]config.MCPServerConfig{
|
||||||
"test-server": {
|
"test-server": {
|
||||||
Enabled: true,
|
Enabled: true,
|
||||||
|
|
@ -228,12 +230,20 @@ func TestNewManager_InitialState(t *testing.T) {
|
||||||
func TestLoadFromMCPConfig_DisabledOrEmptyServers(t *testing.T) {
|
func TestLoadFromMCPConfig_DisabledOrEmptyServers(t *testing.T) {
|
||||||
mgr := NewManager()
|
mgr := NewManager()
|
||||||
|
|
||||||
err := mgr.LoadFromMCPConfig(context.Background(), config.MCPConfig{Enabled: false}, "/tmp")
|
err := mgr.LoadFromMCPConfig(
|
||||||
|
context.Background(),
|
||||||
|
config.MCPConfig{ToolConfig: config.ToolConfig{Enabled: false}},
|
||||||
|
"/tmp",
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("expected nil error when MCP disabled, got: %v", err)
|
t.Fatalf("expected nil error when MCP disabled, got: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = mgr.LoadFromMCPConfig(context.Background(), config.MCPConfig{Enabled: true}, "/tmp")
|
err = mgr.LoadFromMCPConfig(
|
||||||
|
context.Background(),
|
||||||
|
config.MCPConfig{ToolConfig: config.ToolConfig{Enabled: true}},
|
||||||
|
"/tmp",
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("expected nil error when no servers configured, got: %v", err)
|
t.Fatalf("expected nil error when no servers configured, got: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue