* Add tools enable or diable config

This commit is contained in:
lxowalle 2026-03-04 16:39:09 +08:00
parent b82bb9acc0
commit ad8bb3d9b7
5 changed files with 351 additions and 90 deletions

View file

@ -223,19 +223,24 @@ func setupCronTool(
// Create cron service
cronService := cron.NewCronService(cronStorePath, nil)
// Create and register CronTool
// Create and register CronTool if enabled
var cronTool *tools.CronTool
if cfg.Tools.IsToolEnabled("cron") {
cronTool, err := tools.NewCronTool(cronService, agentLoop, msgBus, workspace, restrict, execTimeout, cfg)
if err != nil {
log.Fatalf("Critical error during CronTool initialization: %v", err)
}
agentLoop.RegisterTool(cronTool)
}
// Set the onJob handler
// Set onJob handler
if cronTool != nil {
cronService.SetOnJob(func(job *cron.CronJob) (string, error) {
result := cronTool.ExecuteJob(context.Background(), job)
return result, nil
})
}
return cronService
}

View file

@ -59,17 +59,30 @@ func NewAgentInstance(
allowWritePaths := compilePatterns(cfg.Tools.AllowWritePaths)
toolsRegistry := tools.NewToolRegistry()
if cfg.Tools.IsToolEnabled("read_file") {
toolsRegistry.Register(tools.NewReadFileTool(workspace, readRestrict, allowReadPaths))
}
if cfg.Tools.IsToolEnabled("write_file") {
toolsRegistry.Register(tools.NewWriteFileTool(workspace, restrict, allowWritePaths))
}
if cfg.Tools.IsToolEnabled("list_dir") {
toolsRegistry.Register(tools.NewListDirTool(workspace, readRestrict, allowReadPaths))
}
if cfg.Tools.IsToolEnabled("exec") {
execTool, err := tools.NewExecToolWithConfig(workspace, restrict, cfg)
if err != nil {
log.Fatalf("Critical error: unable to initialize exec tool: %v", err)
}
toolsRegistry.Register(execTool)
}
if cfg.Tools.IsToolEnabled("edit_file") {
toolsRegistry.Register(tools.NewEditFileTool(workspace, restrict, allowWritePaths))
}
if cfg.Tools.IsToolEnabled("append_file") {
toolsRegistry.Register(tools.NewAppendFileTool(workspace, restrict, allowWritePaths))
}
sessionsDir := filepath.Join(workspace, "sessions")
sessionsManager := session.NewSessionManager(sessionsDir)

View file

@ -105,6 +105,7 @@ func registerSharedTools(
}
// Web tools
if cfg.Tools.IsToolEnabled("web_search") {
searchTool, err := tools.NewWebSearchTool(tools.WebSearchToolOptions{
BraveAPIKey: cfg.Tools.Web.Brave.APIKey,
BraveMaxResults: cfg.Tools.Web.Brave.MaxResults,
@ -130,18 +131,26 @@ func registerSharedTools(
} else if searchTool != nil {
agent.Tools.Register(searchTool)
}
}
if cfg.Tools.IsToolEnabled("web_fetch") {
fetchTool, err := tools.NewWebFetchToolWithProxy(50000, cfg.Tools.Web.Proxy, cfg.Tools.Web.FetchLimitBytes)
if err != nil {
logger.ErrorCF("agent", "Failed to create web fetch tool", map[string]any{"error": err.Error()})
} else {
agent.Tools.Register(fetchTool)
}
}
// Hardware tools (I2C, SPI) - Linux only, returns error on other platforms
if cfg.Tools.IsToolEnabled("i2c") {
agent.Tools.Register(tools.NewI2CTool())
}
if cfg.Tools.IsToolEnabled("spi") {
agent.Tools.Register(tools.NewSPITool())
}
// Message tool
if cfg.Tools.IsToolEnabled("message") {
messageTool := tools.NewMessageTool()
messageTool.SetSendCallback(func(channel, chatID, content string) error {
pubCtx, pubCancel := context.WithTimeout(context.Background(), 5*time.Second)
@ -153,6 +162,7 @@ func registerSharedTools(
})
})
agent.Tools.Register(messageTool)
}
// Skill discovery and installation tools
registryMgr := skills.NewRegistryManagerFromConfig(skills.RegistryConfig{
@ -163,10 +173,15 @@ func registerSharedTools(
cfg.Tools.Skills.SearchCache.MaxSize,
time.Duration(cfg.Tools.Skills.SearchCache.TTLSeconds)*time.Second,
)
if cfg.Tools.IsToolEnabled("find_skills") {
agent.Tools.Register(tools.NewFindSkillsTool(registryMgr, searchCache))
}
if cfg.Tools.IsToolEnabled("install_skill") {
agent.Tools.Register(tools.NewInstallSkillTool(registryMgr, agent.Workspace))
}
// Spawn tool with allowlist checker
if cfg.Tools.IsToolEnabled("spawn") {
subagentManager := tools.NewSubagentManager(provider, agent.Model, agent.Workspace, msgBus)
subagentManager.SetLLMOptions(agent.MaxTokens, agent.Temperature)
spawnTool := tools.NewSpawnTool(subagentManager)
@ -177,6 +192,7 @@ func registerSharedTools(
agent.Tools.Register(spawnTool)
}
}
}
func (al *AgentLoop) Run(ctx context.Context) error {
al.running.Store(true)
@ -224,6 +240,7 @@ func (al *AgentLoop) Run(ctx context.Context) error {
if !ok {
continue
}
if al.cfg.Tools.IsToolEnabled("mcp") {
mcpTool := tools.NewMCPTool(mcpManager, serverName, tool)
agent.Tools.Register(mcpTool)
totalRegistrations++
@ -237,6 +254,7 @@ func (al *AgentLoop) Run(ctx context.Context) error {
}
}
}
}
logger.InfoCF("agent", "MCP tools registered successfully",
map[string]any{
"server_count": len(servers),

View file

@ -523,6 +523,10 @@ type GatewayConfig struct {
Port int `json:"port" env:"PICOCLAW_GATEWAY_PORT"`
}
type ToolConfig struct {
Enabled bool `json:"enabled"`
}
type BraveConfig struct {
Enabled bool `json:"enabled" env:"PICOCLAW_TOOLS_WEB_BRAVE_ENABLED"`
APIKey string `json:"api_key" env:"PICOCLAW_TOOLS_WEB_BRAVE_API_KEY"`
@ -558,6 +562,7 @@ type GLMSearchConfig struct {
}
type WebToolsConfig struct {
ToolConfig
Brave BraveConfig `json:"brave"`
Tavily TavilyConfig `json:"tavily"`
DuckDuckGo DuckDuckGoConfig `json:"duckduckgo"`
@ -570,17 +575,26 @@ type WebToolsConfig struct {
}
type CronToolsConfig struct {
ToolConfig
ExecTimeoutMinutes int `json:"exec_timeout_minutes" env:"PICOCLAW_TOOLS_CRON_EXEC_TIMEOUT_MINUTES"` // 0 means no timeout
}
type ExecConfig struct {
ToolConfig
EnableDenyPatterns bool `json:"enable_deny_patterns" env:"PICOCLAW_TOOLS_EXEC_ENABLE_DENY_PATTERNS"`
CustomDenyPatterns []string `json:"custom_deny_patterns" env:"PICOCLAW_TOOLS_EXEC_CUSTOM_DENY_PATTERNS"`
CustomAllowPatterns []string `json:"custom_allow_patterns" env:"PICOCLAW_TOOLS_EXEC_CUSTOM_ALLOW_PATTERNS"`
}
type SkillsToolsConfig struct {
ToolConfig
Registries SkillsRegistriesConfig `json:"registries"`
MaxConcurrentSearches int `json:"max_concurrent_searches" env:"PICOCLAW_SKILLS_MAX_CONCURRENT_SEARCHES"`
SearchCache SearchCacheConfig `json:"search_cache"`
}
type MediaCleanupConfig struct {
Enabled bool `json:"enabled" env:"PICOCLAW_MEDIA_CLEANUP_ENABLED"`
ToolConfig
MaxAge int `json:"max_age_minutes" env:"PICOCLAW_MEDIA_CLEANUP_MAX_AGE"`
Interval int `json:"interval_minutes" env:"PICOCLAW_MEDIA_CLEANUP_INTERVAL"`
}
@ -594,12 +608,116 @@ type ToolsConfig struct {
Skills SkillsToolsConfig `json:"skills"`
MediaCleanup MediaCleanupConfig `json:"media_cleanup"`
MCP MCPConfig `json:"mcp"`
AppendFile ToolConfig `json:"append_file"`
CronTool ToolConfig `json:"cron_tool"`
EditFile ToolConfig `json:"edit_file"`
ExecTool ToolConfig `json:"exec_tool"`
FindSkills ToolConfig `json:"find_skills"`
I2C ToolConfig `json:"i2c"`
InstallSkill ToolConfig `json:"install_skill"`
ListDir ToolConfig `json:"list_dir"`
Message ToolConfig `json:"message"`
ReadFile ToolConfig `json:"read_file"`
Spawn ToolConfig `json:"spawn"`
SPI ToolConfig `json:"spi"`
Subagent ToolConfig `json:"subagent"`
WebFetch ToolConfig `json:"web_fetch"`
WebSearch ToolConfig `json:"web_search"`
WriteFile ToolConfig `json:"write_file"`
}
type SkillsToolsConfig struct {
Registries SkillsRegistriesConfig `json:"registries"`
MaxConcurrentSearches int `json:"max_concurrent_searches" env:"PICOCLAW_SKILLS_MAX_CONCURRENT_SEARCHES"`
SearchCache SearchCacheConfig `json:"search_cache"`
type ToolsConfigRaw struct {
AllowReadPaths []string `json:"allow_read_paths"`
AllowWritePaths []string `json:"allow_write_paths"`
Web WebToolsConfig `json:"web"`
Cron CronToolsConfig `json:"cron"`
Exec ExecConfig `json:"exec"`
Skills SkillsToolsConfig `json:"skills"`
MediaCleanup MediaCleanupConfig `json:"media_cleanup"`
MCP MCPConfig `json:"mcp"`
AppendFile *ToolConfig `json:"append_file,omitempty"`
CronTool *ToolConfig `json:"cron_tool,omitempty"`
EditFile *ToolConfig `json:"edit_file,omitempty"`
ExecTool *ToolConfig `json:"exec_tool,omitempty"`
FindSkills *ToolConfig `json:"find_skills,omitempty"`
I2C *ToolConfig `json:"i2c,omitempty"`
InstallSkill *ToolConfig `json:"install_skill,omitempty"`
ListDir *ToolConfig `json:"list_dir,omitempty"`
Message *ToolConfig `json:"message,omitempty"`
ReadFile *ToolConfig `json:"read_file,omitempty"`
Spawn *ToolConfig `json:"spawn,omitempty"`
SPI *ToolConfig `json:"spi,omitempty"`
Subagent *ToolConfig `json:"subagent,omitempty"`
WebFetch *ToolConfig `json:"web_fetch,omitempty"`
WebSearch *ToolConfig `json:"web_search,omitempty"`
WriteFile *ToolConfig `json:"write_file,omitempty"`
}
func (t *ToolsConfig) UnmarshalJSON(data []byte) error {
var raw ToolsConfigRaw
if err := json.Unmarshal(data, &raw); err != nil {
return err
}
t.AllowReadPaths = raw.AllowReadPaths
t.AllowWritePaths = raw.AllowWritePaths
t.Web = raw.Web
t.Cron = raw.Cron
t.Exec = raw.Exec
t.Skills = raw.Skills
t.MediaCleanup = raw.MediaCleanup
t.MCP = raw.MCP
if raw.AppendFile != nil {
t.AppendFile = *raw.AppendFile
}
if raw.CronTool != nil {
t.CronTool = *raw.CronTool
}
if raw.EditFile != nil {
t.EditFile = *raw.EditFile
}
if raw.ExecTool != nil {
t.ExecTool = *raw.ExecTool
}
if raw.FindSkills != nil {
t.FindSkills = *raw.FindSkills
}
if raw.I2C != nil {
t.I2C = *raw.I2C
}
if raw.InstallSkill != nil {
t.InstallSkill = *raw.InstallSkill
}
if raw.ListDir != nil {
t.ListDir = *raw.ListDir
}
if raw.Message != nil {
t.Message = *raw.Message
}
if raw.ReadFile != nil {
t.ReadFile = *raw.ReadFile
}
if raw.Spawn != nil {
t.Spawn = *raw.Spawn
}
if raw.SPI != nil {
t.SPI = *raw.SPI
}
if raw.Subagent != nil {
t.Subagent = *raw.Subagent
}
if raw.WebFetch != nil {
t.WebFetch = *raw.WebFetch
}
if raw.WebSearch != nil {
t.WebSearch = *raw.WebSearch
}
if raw.WriteFile != nil {
t.WriteFile = *raw.WriteFile
}
return nil
}
type SearchCacheConfig struct {
@ -832,3 +950,48 @@ func (c *Config) ValidateModelList() error {
}
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 "web_search":
return t.WebSearch.Enabled
case "write_file":
return t.WriteFile.Enabled
default:
return true
}
}

View file

@ -322,11 +322,16 @@ func DefaultConfig() *Config {
},
Tools: ToolsConfig{
MediaCleanup: MediaCleanupConfig{
ToolConfig: ToolConfig{
Enabled: true,
},
MaxAge: 30,
Interval: 5,
},
Web: WebToolsConfig{
ToolConfig: ToolConfig{
Enabled: true,
},
Proxy: "",
FetchLimitBytes: 10 * 1024 * 1024, // 10MB by default
Brave: BraveConfig{
@ -352,12 +357,21 @@ func DefaultConfig() *Config {
},
},
Cron: CronToolsConfig{
ToolConfig: ToolConfig{
Enabled: true,
},
ExecTimeoutMinutes: 5,
},
Exec: ExecConfig{
ToolConfig: ToolConfig{
Enabled: true,
},
EnableDenyPatterns: true,
},
Skills: SkillsToolsConfig{
ToolConfig: ToolConfig{
Enabled: true,
},
Registries: SkillsRegistriesConfig{
ClawHub: ClawHubRegistryConfig{
Enabled: true,
@ -374,6 +388,54 @@ func DefaultConfig() *Config {
Enabled: false,
Servers: map[string]MCPServerConfig{},
},
AppendFile: ToolConfig{
Enabled: true,
},
CronTool: ToolConfig{
Enabled: true,
},
EditFile: ToolConfig{
Enabled: true,
},
ExecTool: ToolConfig{
Enabled: true,
},
FindSkills: ToolConfig{
Enabled: true,
},
I2C: ToolConfig{
Enabled: false, // Hardware tool - Linux only
},
InstallSkill: ToolConfig{
Enabled: true,
},
ListDir: ToolConfig{
Enabled: false,
},
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,
},
WebSearch: ToolConfig{
Enabled: true,
},
WriteFile: ToolConfig{
Enabled: true,
},
},
Heartbeat: HeartbeatConfig{
Enabled: true,