feat: 🎸 add allow patterns to config file
This commit is contained in:
parent
0d16525fab
commit
8e4961e252
8 changed files with 71 additions and 8 deletions
|
|
@ -563,7 +563,7 @@ func gatewayCmd() {
|
|||
|
||||
// Setup cron tool and service
|
||||
execTimeout := time.Duration(cfg.Tools.Cron.ExecTimeoutMinutes) * time.Minute
|
||||
cronService := setupCronTool(agentLoop, msgBus, cfg.WorkspacePath(), cfg.Agents.Defaults.RestrictToWorkspace, execTimeout)
|
||||
cronService := setupCronTool(agentLoop, msgBus, cfg.WorkspacePath(), cfg.Agents.Defaults.RestrictToWorkspace, execTimeout, cfg.Agents.Defaults.AllowPatterns)
|
||||
|
||||
heartbeatService := heartbeat.NewHeartbeatService(
|
||||
cfg.WorkspacePath(),
|
||||
|
|
@ -988,14 +988,14 @@ func getConfigPath() string {
|
|||
return filepath.Join(home, ".picoclaw", "config.json")
|
||||
}
|
||||
|
||||
func setupCronTool(agentLoop *agent.AgentLoop, msgBus *bus.MessageBus, workspace string, restrict bool, execTimeout time.Duration) *cron.CronService {
|
||||
func setupCronTool(agentLoop *agent.AgentLoop, msgBus *bus.MessageBus, workspace string, restrict bool, execTimeout time.Duration, allowPatterns []string) *cron.CronService {
|
||||
cronStorePath := filepath.Join(workspace, "cron", "jobs.json")
|
||||
|
||||
// Create cron service
|
||||
cronService := cron.NewCronService(cronStorePath, nil)
|
||||
|
||||
// Create and register CronTool
|
||||
cronTool := tools.NewCronTool(cronService, agentLoop, msgBus, workspace, restrict, execTimeout)
|
||||
cronTool := tools.NewCronTool(cronService, agentLoop, msgBus, workspace, restrict, execTimeout, allowPatterns...)
|
||||
agentLoop.RegisterTool(cronTool)
|
||||
|
||||
// Set the onJob handler
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
"defaults": {
|
||||
"workspace": "~/.picoclaw/workspace",
|
||||
"restrict_to_workspace": true,
|
||||
"provider": "",
|
||||
"allow_patterns": [],
|
||||
"model": "glm-4.7",
|
||||
"max_tokens": 8192,
|
||||
"temperature": 0.7,
|
||||
|
|
@ -122,6 +124,10 @@
|
|||
"api_key": "YOUR_BRAVE_API_KEY",
|
||||
"max_results": 5
|
||||
},
|
||||
"duckduckgo": {
|
||||
"enabled": true,
|
||||
"max_results": 5
|
||||
},
|
||||
"perplexity": {
|
||||
"enabled": false,
|
||||
"api_key": "pplx-xxx",
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ func createToolRegistry(workspace string, restrict bool, cfg *config.Config, msg
|
|||
registry.Register(tools.NewAppendFileTool(workspace, restrict))
|
||||
|
||||
// Shell execution
|
||||
registry.Register(tools.NewExecTool(workspace, restrict))
|
||||
registry.Register(tools.NewExecTool(workspace, restrict, cfg.Agents.Defaults.AllowPatterns...))
|
||||
|
||||
if searchTool := tools.NewWebSearchTool(tools.WebSearchToolOptions{
|
||||
BraveAPIKey: cfg.Tools.Web.Brave.APIKey,
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ type AgentsConfig struct {
|
|||
type AgentDefaults struct {
|
||||
Workspace string `json:"workspace" env:"PICOCLAW_AGENTS_DEFAULTS_WORKSPACE"`
|
||||
RestrictToWorkspace bool `json:"restrict_to_workspace" env:"PICOCLAW_AGENTS_DEFAULTS_RESTRICT_TO_WORKSPACE"`
|
||||
AllowPatterns []string `json:"allow_patterns" env:"PICOCLAW_AGENTS_DEFAULTS_ALLOW_PATTERNS" envSeparator:","`
|
||||
Provider string `json:"provider" env:"PICOCLAW_AGENTS_DEFAULTS_PROVIDER"`
|
||||
Model string `json:"model" env:"PICOCLAW_AGENTS_DEFAULTS_MODEL"`
|
||||
MaxTokens int `json:"max_tokens" env:"PICOCLAW_AGENTS_DEFAULTS_MAX_TOKENS"`
|
||||
|
|
@ -233,6 +234,7 @@ func DefaultConfig() *Config {
|
|||
Defaults: AgentDefaults{
|
||||
Workspace: "~/.picoclaw/workspace",
|
||||
RestrictToWorkspace: true,
|
||||
AllowPatterns: []string{},
|
||||
Provider: "",
|
||||
Model: "glm-4.7",
|
||||
MaxTokens: 8192,
|
||||
|
|
|
|||
|
|
@ -84,6 +84,9 @@ func ConvertConfig(data map[string]interface{}) (*config.Config, []string, error
|
|||
if v, ok := getString(defaults, "workspace"); ok {
|
||||
cfg.Agents.Defaults.Workspace = rewriteWorkspacePath(v)
|
||||
}
|
||||
if v := getStringSlice(defaults, "allow_patterns"); len(v) > 0 {
|
||||
cfg.Agents.Defaults.AllowPatterns = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -280,6 +283,10 @@ func MergeConfig(existing, incoming *config.Config) *config.Config {
|
|||
existing.Tools.Web.Brave = incoming.Tools.Web.Brave
|
||||
}
|
||||
|
||||
if len(existing.Agents.Defaults.AllowPatterns) == 0 && len(incoming.Agents.Defaults.AllowPatterns) > 0 {
|
||||
existing.Agents.Defaults.AllowPatterns = incoming.Agents.Defaults.AllowPatterns
|
||||
}
|
||||
|
||||
return existing
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -261,6 +261,7 @@ func TestConvertConfig(t *testing.T) {
|
|||
"temperature": 0.5,
|
||||
"max_tool_iterations": float64(10),
|
||||
"workspace": "~/.openclaw/workspace",
|
||||
"allow_patterns": []interface{}{"^ls(\\s|$)", "^pwd$"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
@ -281,6 +282,15 @@ func TestConvertConfig(t *testing.T) {
|
|||
if cfg.Agents.Defaults.Workspace != "~/.picoclaw/workspace" {
|
||||
t.Errorf("Workspace = %q, want %q", cfg.Agents.Defaults.Workspace, "~/.picoclaw/workspace")
|
||||
}
|
||||
if len(cfg.Agents.Defaults.AllowPatterns) != 2 {
|
||||
t.Fatalf("AllowPatterns length = %d, want 2", len(cfg.Agents.Defaults.AllowPatterns))
|
||||
}
|
||||
if cfg.Agents.Defaults.AllowPatterns[0] != "^ls(\\s|$)" {
|
||||
t.Errorf("AllowPatterns[0] = %q, want %q", cfg.Agents.Defaults.AllowPatterns[0], "^ls(\\s|$)")
|
||||
}
|
||||
if cfg.Agents.Defaults.AllowPatterns[1] != "^pwd$" {
|
||||
t.Errorf("AllowPatterns[1] = %q, want %q", cfg.Agents.Defaults.AllowPatterns[1], "^pwd$")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("empty config", func(t *testing.T) {
|
||||
|
|
@ -361,6 +371,36 @@ func TestMergeConfig(t *testing.T) {
|
|||
t.Errorf("Telegram.Token should be preserved, got %q", result.Channels.Telegram.Token)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("fills empty allow patterns", func(t *testing.T) {
|
||||
existing := config.DefaultConfig()
|
||||
incoming := config.DefaultConfig()
|
||||
incoming.Agents.Defaults.AllowPatterns = []string{"^ls(\\s|$)", "^pwd$"}
|
||||
|
||||
result := MergeConfig(existing, incoming)
|
||||
if len(result.Agents.Defaults.AllowPatterns) != 2 {
|
||||
t.Fatalf("AllowPatterns length = %d, want 2", len(result.Agents.Defaults.AllowPatterns))
|
||||
}
|
||||
if result.Agents.Defaults.AllowPatterns[0] != "^ls(\\s|$)" {
|
||||
t.Errorf("AllowPatterns[0] = %q, want %q", result.Agents.Defaults.AllowPatterns[0], "^ls(\\s|$)")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("preserves existing allow patterns", func(t *testing.T) {
|
||||
existing := config.DefaultConfig()
|
||||
existing.Agents.Defaults.AllowPatterns = []string{"^cat\\s+README\\.md$"}
|
||||
|
||||
incoming := config.DefaultConfig()
|
||||
incoming.Agents.Defaults.AllowPatterns = []string{"^ls(\\s|$)"}
|
||||
|
||||
result := MergeConfig(existing, incoming)
|
||||
if len(result.Agents.Defaults.AllowPatterns) != 1 {
|
||||
t.Fatalf("AllowPatterns length = %d, want 1", len(result.Agents.Defaults.AllowPatterns))
|
||||
}
|
||||
if result.Agents.Defaults.AllowPatterns[0] != "^cat\\s+README\\.md$" {
|
||||
t.Errorf("AllowPatterns[0] should be preserved, got %q", result.Agents.Defaults.AllowPatterns[0])
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestPlanWorkspaceMigration(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -29,8 +29,8 @@ type CronTool struct {
|
|||
|
||||
// NewCronTool creates a new CronTool
|
||||
// execTimeout: 0 means no timeout, >0 sets the timeout duration
|
||||
func NewCronTool(cronService *cron.CronService, executor JobExecutor, msgBus *bus.MessageBus, workspace string, restrict bool, execTimeout time.Duration) *CronTool {
|
||||
execTool := NewExecTool(workspace, restrict)
|
||||
func NewCronTool(cronService *cron.CronService, executor JobExecutor, msgBus *bus.MessageBus, workspace string, restrict bool, execTimeout time.Duration, allowPatterns ...string) *CronTool {
|
||||
execTool := NewExecTool(workspace, restrict, allowPatterns...)
|
||||
execTool.SetTimeout(execTimeout) // 0 means no timeout
|
||||
return &CronTool{
|
||||
cronService: cronService,
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ type ExecTool struct {
|
|||
restrictToWorkspace bool
|
||||
}
|
||||
|
||||
func NewExecTool(workingDir string, restrict bool) *ExecTool {
|
||||
func NewExecTool(workingDir string, restrict bool, allowPatterns ...string) *ExecTool {
|
||||
denyPatterns := []*regexp.Regexp{
|
||||
regexp.MustCompile(`\brm\s+-[rf]{1,2}\b`),
|
||||
regexp.MustCompile(`\bdel\s+/[fq]\b`),
|
||||
|
|
@ -33,11 +33,19 @@ func NewExecTool(workingDir string, restrict bool) *ExecTool {
|
|||
regexp.MustCompile(`:\(\)\s*\{.*\};\s*:`),
|
||||
}
|
||||
|
||||
var allowRegex []*regexp.Regexp
|
||||
if len(allowPatterns) > 0 {
|
||||
allowRegex = make([]*regexp.Regexp, 0, len(allowPatterns))
|
||||
for _, p := range allowPatterns {
|
||||
allowRegex = append(allowRegex, regexp.MustCompile(p))
|
||||
}
|
||||
}
|
||||
|
||||
return &ExecTool{
|
||||
workingDir: workingDir,
|
||||
timeout: 60 * time.Second,
|
||||
denyPatterns: denyPatterns,
|
||||
allowPatterns: nil,
|
||||
allowPatterns: allowRegex,
|
||||
restrictToWorkspace: restrict,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue