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
|
// Setup cron tool and service
|
||||||
execTimeout := time.Duration(cfg.Tools.Cron.ExecTimeoutMinutes) * time.Minute
|
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(
|
heartbeatService := heartbeat.NewHeartbeatService(
|
||||||
cfg.WorkspacePath(),
|
cfg.WorkspacePath(),
|
||||||
|
|
@ -988,14 +988,14 @@ func getConfigPath() string {
|
||||||
return filepath.Join(home, ".picoclaw", "config.json")
|
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")
|
cronStorePath := filepath.Join(workspace, "cron", "jobs.json")
|
||||||
|
|
||||||
// Create cron service
|
// Create cron service
|
||||||
cronService := cron.NewCronService(cronStorePath, nil)
|
cronService := cron.NewCronService(cronStorePath, nil)
|
||||||
|
|
||||||
// Create and register CronTool
|
// 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)
|
agentLoop.RegisterTool(cronTool)
|
||||||
|
|
||||||
// Set the onJob handler
|
// Set the onJob handler
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@
|
||||||
"defaults": {
|
"defaults": {
|
||||||
"workspace": "~/.picoclaw/workspace",
|
"workspace": "~/.picoclaw/workspace",
|
||||||
"restrict_to_workspace": true,
|
"restrict_to_workspace": true,
|
||||||
|
"provider": "",
|
||||||
|
"allow_patterns": [],
|
||||||
"model": "glm-4.7",
|
"model": "glm-4.7",
|
||||||
"max_tokens": 8192,
|
"max_tokens": 8192,
|
||||||
"temperature": 0.7,
|
"temperature": 0.7,
|
||||||
|
|
@ -122,6 +124,10 @@
|
||||||
"api_key": "YOUR_BRAVE_API_KEY",
|
"api_key": "YOUR_BRAVE_API_KEY",
|
||||||
"max_results": 5
|
"max_results": 5
|
||||||
},
|
},
|
||||||
|
"duckduckgo": {
|
||||||
|
"enabled": true,
|
||||||
|
"max_results": 5
|
||||||
|
},
|
||||||
"perplexity": {
|
"perplexity": {
|
||||||
"enabled": false,
|
"enabled": false,
|
||||||
"api_key": "pplx-xxx",
|
"api_key": "pplx-xxx",
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,7 @@ func createToolRegistry(workspace string, restrict bool, cfg *config.Config, msg
|
||||||
registry.Register(tools.NewAppendFileTool(workspace, restrict))
|
registry.Register(tools.NewAppendFileTool(workspace, restrict))
|
||||||
|
|
||||||
// Shell execution
|
// Shell execution
|
||||||
registry.Register(tools.NewExecTool(workspace, restrict))
|
registry.Register(tools.NewExecTool(workspace, restrict, cfg.Agents.Defaults.AllowPatterns...))
|
||||||
|
|
||||||
if searchTool := tools.NewWebSearchTool(tools.WebSearchToolOptions{
|
if searchTool := tools.NewWebSearchTool(tools.WebSearchToolOptions{
|
||||||
BraveAPIKey: cfg.Tools.Web.Brave.APIKey,
|
BraveAPIKey: cfg.Tools.Web.Brave.APIKey,
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,7 @@ type AgentsConfig struct {
|
||||||
type AgentDefaults struct {
|
type AgentDefaults struct {
|
||||||
Workspace string `json:"workspace" env:"PICOCLAW_AGENTS_DEFAULTS_WORKSPACE"`
|
Workspace string `json:"workspace" env:"PICOCLAW_AGENTS_DEFAULTS_WORKSPACE"`
|
||||||
RestrictToWorkspace bool `json:"restrict_to_workspace" env:"PICOCLAW_AGENTS_DEFAULTS_RESTRICT_TO_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"`
|
Provider string `json:"provider" env:"PICOCLAW_AGENTS_DEFAULTS_PROVIDER"`
|
||||||
Model string `json:"model" env:"PICOCLAW_AGENTS_DEFAULTS_MODEL"`
|
Model string `json:"model" env:"PICOCLAW_AGENTS_DEFAULTS_MODEL"`
|
||||||
MaxTokens int `json:"max_tokens" env:"PICOCLAW_AGENTS_DEFAULTS_MAX_TOKENS"`
|
MaxTokens int `json:"max_tokens" env:"PICOCLAW_AGENTS_DEFAULTS_MAX_TOKENS"`
|
||||||
|
|
@ -233,6 +234,7 @@ func DefaultConfig() *Config {
|
||||||
Defaults: AgentDefaults{
|
Defaults: AgentDefaults{
|
||||||
Workspace: "~/.picoclaw/workspace",
|
Workspace: "~/.picoclaw/workspace",
|
||||||
RestrictToWorkspace: true,
|
RestrictToWorkspace: true,
|
||||||
|
AllowPatterns: []string{},
|
||||||
Provider: "",
|
Provider: "",
|
||||||
Model: "glm-4.7",
|
Model: "glm-4.7",
|
||||||
MaxTokens: 8192,
|
MaxTokens: 8192,
|
||||||
|
|
|
||||||
|
|
@ -84,6 +84,9 @@ func ConvertConfig(data map[string]interface{}) (*config.Config, []string, error
|
||||||
if v, ok := getString(defaults, "workspace"); ok {
|
if v, ok := getString(defaults, "workspace"); ok {
|
||||||
cfg.Agents.Defaults.Workspace = rewriteWorkspacePath(v)
|
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
|
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
|
return existing
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -261,6 +261,7 @@ func TestConvertConfig(t *testing.T) {
|
||||||
"temperature": 0.5,
|
"temperature": 0.5,
|
||||||
"max_tool_iterations": float64(10),
|
"max_tool_iterations": float64(10),
|
||||||
"workspace": "~/.openclaw/workspace",
|
"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" {
|
if cfg.Agents.Defaults.Workspace != "~/.picoclaw/workspace" {
|
||||||
t.Errorf("Workspace = %q, want %q", 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) {
|
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.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) {
|
func TestPlanWorkspaceMigration(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -29,8 +29,8 @@ type CronTool struct {
|
||||||
|
|
||||||
// NewCronTool creates a new CronTool
|
// NewCronTool creates a new CronTool
|
||||||
// execTimeout: 0 means no timeout, >0 sets the timeout duration
|
// 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 {
|
func NewCronTool(cronService *cron.CronService, executor JobExecutor, msgBus *bus.MessageBus, workspace string, restrict bool, execTimeout time.Duration, allowPatterns ...string) *CronTool {
|
||||||
execTool := NewExecTool(workspace, restrict)
|
execTool := NewExecTool(workspace, restrict, allowPatterns...)
|
||||||
execTool.SetTimeout(execTimeout) // 0 means no timeout
|
execTool.SetTimeout(execTimeout) // 0 means no timeout
|
||||||
return &CronTool{
|
return &CronTool{
|
||||||
cronService: cronService,
|
cronService: cronService,
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ type ExecTool struct {
|
||||||
restrictToWorkspace bool
|
restrictToWorkspace bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewExecTool(workingDir string, restrict bool) *ExecTool {
|
func NewExecTool(workingDir string, restrict bool, allowPatterns ...string) *ExecTool {
|
||||||
denyPatterns := []*regexp.Regexp{
|
denyPatterns := []*regexp.Regexp{
|
||||||
regexp.MustCompile(`\brm\s+-[rf]{1,2}\b`),
|
regexp.MustCompile(`\brm\s+-[rf]{1,2}\b`),
|
||||||
regexp.MustCompile(`\bdel\s+/[fq]\b`),
|
regexp.MustCompile(`\bdel\s+/[fq]\b`),
|
||||||
|
|
@ -33,11 +33,19 @@ func NewExecTool(workingDir string, restrict bool) *ExecTool {
|
||||||
regexp.MustCompile(`:\(\)\s*\{.*\};\s*:`),
|
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{
|
return &ExecTool{
|
||||||
workingDir: workingDir,
|
workingDir: workingDir,
|
||||||
timeout: 60 * time.Second,
|
timeout: 60 * time.Second,
|
||||||
denyPatterns: denyPatterns,
|
denyPatterns: denyPatterns,
|
||||||
allowPatterns: nil,
|
allowPatterns: allowRegex,
|
||||||
restrictToWorkspace: restrict,
|
restrictToWorkspace: restrict,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue