From 82856bc57aa0af52dc8c3f9b563b90af2d030126 Mon Sep 17 00:00:00 2001 From: yinwm Date: Sun, 15 Feb 2026 18:41:39 +0800 Subject: [PATCH] feat(cron): add configurable execution timeout for cron jobs Add a new configuration option `exec_timeout_minutes` under the `tools.cron` section to control the maximum execution time for cron jobs. The default timeout is set to 5 minutes, which is appropriate for LLM operations. The configuration can be set in the config file or via the `PICOCLAW_TOOLS_CRON_EXEC_TIMEOUT_MINUTES` environment variable. A value of 0 disables the timeout entirely. This change improves system reliability by preventing cron jobs from running indefinitely in case of unexpected failures or hanging processes. --- README.ja.md | 6 ++++++ README.md | 3 +++ README.zh.md | 6 ++++++ cmd/picoclaw/main.go | 6 +++--- config/config.example.json | 3 +++ pkg/config/config.go | 10 +++++++++- pkg/tools/cron.go | 8 ++++++-- 7 files changed, 36 insertions(+), 6 deletions(-) diff --git a/README.ja.md b/README.ja.md index 48105ce2f..5e4e49411 100644 --- a/README.ja.md +++ b/README.ja.md @@ -195,6 +195,9 @@ picoclaw onboard "api_key": "YOUR_BRAVE_API_KEY", "max_results": 5 } + }, + "cron": { + "exec_timeout_minutes": 5 } }, "heartbeat": { @@ -646,6 +649,9 @@ HEARTBEAT_OK 応答 ユーザーが直接結果を受け取る "search": { "apiKey": "BSA..." } + }, + "cron": { + "exec_timeout_minutes": 5 } }, "heartbeat": { diff --git a/README.md b/README.md index 2ba70881b..1b7537fc9 100644 --- a/README.md +++ b/README.md @@ -697,6 +697,9 @@ picoclaw agent -m "Hello" "search": { "api_key": "BSA..." } + }, + "cron": { + "exec_timeout_minutes": 5 } }, "heartbeat": { diff --git a/README.zh.md b/README.zh.md index f2c9bf780..877cb0f5d 100644 --- a/README.zh.md +++ b/README.zh.md @@ -217,6 +217,9 @@ picoclaw onboard "api_key": "YOUR_BRAVE_API_KEY", "max_results": 5 } + }, + "cron": { + "exec_timeout_minutes": 5 } } } @@ -625,6 +628,9 @@ picoclaw agent -m "你好" "search": { "api_key": "BSA..." } + }, + "cron": { + "exec_timeout_minutes": 5 } }, "heartbeat": { diff --git a/cmd/picoclaw/main.go b/cmd/picoclaw/main.go index 21246cf41..8225931c8 100644 --- a/cmd/picoclaw/main.go +++ b/cmd/picoclaw/main.go @@ -669,7 +669,7 @@ func gatewayCmd() { }) // Setup cron tool and service - cronService := setupCronTool(agentLoop, msgBus, cfg.WorkspacePath()) + cronService := setupCronTool(agentLoop, msgBus, cfg.WorkspacePath(), time.Duration(cfg.Tools.Cron.ExecTimeoutMinutes)*time.Minute) heartbeatService := heartbeat.NewHeartbeatService( cfg.WorkspacePath(), @@ -1069,14 +1069,14 @@ func getConfigPath() string { return filepath.Join(home, ".picoclaw", "config.json") } -func setupCronTool(agentLoop *agent.AgentLoop, msgBus *bus.MessageBus, workspace string) *cron.CronService { +func setupCronTool(agentLoop *agent.AgentLoop, msgBus *bus.MessageBus, workspace string, execTimeout time.Duration) *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) + cronTool := tools.NewCronTool(cronService, agentLoop, msgBus, workspace, execTimeout) agentLoop.RegisterTool(cronTool) // Set the onJob handler diff --git a/config/config.example.json b/config/config.example.json index c71587a04..d56596f24 100644 --- a/config/config.example.json +++ b/config/config.example.json @@ -98,6 +98,9 @@ "api_key": "YOUR_BRAVE_API_KEY", "max_results": 5 } + }, + "cron": { + "exec_timeout_minutes": 5 } }, "heartbeat": { diff --git a/pkg/config/config.go b/pkg/config/config.go index 391120e2d..9acbcce8c 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -173,8 +173,13 @@ type WebToolsConfig struct { Search WebSearchConfig `json:"search"` } +type CronToolsConfig struct { + ExecTimeoutMinutes int `json:"exec_timeout_minutes" env:"PICOCLAW_TOOLS_CRON_EXEC_TIMEOUT_MINUTES"` // 0 means no timeout +} + type ToolsConfig struct { - Web WebToolsConfig `json:"web"` + Web WebToolsConfig `json:"web"` + Cron CronToolsConfig `json:"cron"` } func DefaultConfig() *Config { @@ -262,6 +267,9 @@ func DefaultConfig() *Config { MaxResults: 5, }, }, + Cron: CronToolsConfig{ + ExecTimeoutMinutes: 5, // default 5 minutes for LLM operations + }, }, Heartbeat: HeartbeatConfig{ Enabled: true, diff --git a/pkg/tools/cron.go b/pkg/tools/cron.go index 0ef745e2b..8632b07b9 100644 --- a/pkg/tools/cron.go +++ b/pkg/tools/cron.go @@ -28,12 +28,16 @@ type CronTool struct { } // NewCronTool creates a new CronTool -func NewCronTool(cronService *cron.CronService, executor JobExecutor, msgBus *bus.MessageBus, workspace string) *CronTool { +func NewCronTool(cronService *cron.CronService, executor JobExecutor, msgBus *bus.MessageBus, workspace string, execTimeout time.Duration) *CronTool { + execTool := NewExecTool(workspace, false) + if execTimeout > 0 { + execTool.SetTimeout(execTimeout) + } return &CronTool{ cronService: cronService, executor: executor, msgBus: msgBus, - execTool: NewExecTool(workspace, false), + execTool: execTool, } }