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.
This commit is contained in:
yinwm
2026-02-15 18:41:39 +08:00
parent 14de80d35f
commit 82856bc57a
7 changed files with 36 additions and 6 deletions
+6
View File
@@ -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": {
+3
View File
@@ -697,6 +697,9 @@ picoclaw agent -m "Hello"
"search": {
"api_key": "BSA..."
}
},
"cron": {
"exec_timeout_minutes": 5
}
},
"heartbeat": {
+6
View File
@@ -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": {
+3 -3
View File
@@ -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
+3
View File
@@ -98,6 +98,9 @@
"api_key": "YOUR_BRAVE_API_KEY",
"max_results": 5
}
},
"cron": {
"exec_timeout_minutes": 5
}
},
"heartbeat": {
+9 -1
View File
@@ -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,
+6 -2
View File
@@ -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,
}
}