Class: Clacky::Server::Scheduler
- Inherits:
-
Object
- Object
- Clacky::Server::Scheduler
- Defined in:
- lib/clacky/server/scheduler.rb
Overview
Scheduler reads ~/.clacky/schedules.yml and runs tasks on a cron-like schedule.
It starts a background thread that ticks every 60 seconds, checks all configured schedules, and fires any task whose cron expression matches the current time.
Schedule file format (~/.clacky/schedules.yml):
- name: daily_report
task: daily_report # references ~/.clacky/tasks/daily_report.md
cron: "0 9 * * 1-5" # standard 5-field cron expression
enabled: true # optional, defaults to true
Cron field order: minute hour day-of-month month day-of-week
Constant Summary collapse
- SCHEDULES_FILE =
File.("~/.clacky/schedules.yml")
- TASKS_DIR =
File.("~/.clacky/tasks")
Instance Method Summary collapse
-
#add_schedule(name:, task:, cron:, enabled: true) ⇒ Object
Add or update a schedule entry in schedules.yml.
-
#create_cron_task(name:, content:, cron:, enabled: true) ⇒ Object
Create a task file and its schedule in one step.
-
#delete_cron_task(name) ⇒ Object
Delete a cron-task: remove both the task file and its schedule.
-
#delete_task(task_name) ⇒ Object
Delete a task file and remove all schedules that reference it.
-
#initialize(session_registry:, session_builder:, task_runner:) ⇒ Scheduler
constructor
A new instance of Scheduler.
-
#list_cron_tasks ⇒ Object
Return a merged list of cron-tasks (task content + schedule metadata).
-
#list_tasks ⇒ Object
List all existing task names.
-
#read_task(task_name) ⇒ Object
Read the prompt content of a named task.
-
#remove_schedule(name) ⇒ Object
Remove a schedule entry by name.
- #running? ⇒ Boolean
-
#schedules ⇒ Object
Return all schedules from the config file.
-
#start ⇒ Object
Start the background scheduler thread.
-
#stop ⇒ Object
Stop the background scheduler thread gracefully.
-
#task_file_path(task_name) ⇒ Object
Return the file path for a task.
-
#update_cron_task(name, content: nil, cron: nil, enabled: nil) ⇒ Object
Update a cron-task: optionally update content and/or schedule fields.
-
#update_schedule(name, cron: nil, enabled: nil) ⇒ Object
Update an existing schedule entry (cron and/or enabled).
-
#write_task(task_name, content) ⇒ Object
Write the prompt content for a named task.
Constructor Details
#initialize(session_registry:, session_builder:, task_runner:) ⇒ Scheduler
Returns a new instance of Scheduler.
26 27 28 29 30 31 32 33 34 35 36 |
# File 'lib/clacky/server/scheduler.rb', line 26 def initialize(session_registry:, session_builder:, task_runner:) @registry = session_registry @session_builder = session_builder # callable: (name:, working_dir:) -> session_id # Callable that runs a task on an agent with unified status/save/broadcast # handling — signature: (session_id, agent, &block). Same contract as # the one ChannelManager receives. @task_runner = task_runner @thread = nil @running = false @mutex = Mutex.new end |
Instance Method Details
#add_schedule(name:, task:, cron:, enabled: true) ⇒ Object
Add or update a schedule entry in schedules.yml.
70 71 72 73 74 75 76 77 78 79 80 81 |
# File 'lib/clacky/server/scheduler.rb', line 70 def add_schedule(name:, task:, cron:, enabled: true) list = load_schedules # Remove existing entry with the same name list.reject! { |s| s["name"] == name } list << { "name" => name, "task" => task, "cron" => cron, "enabled" => enabled } save_schedules(list) end |
#create_cron_task(name:, content:, cron:, enabled: true) ⇒ Object
Create a task file and its schedule in one step.
108 109 110 111 |
# File 'lib/clacky/server/scheduler.rb', line 108 def create_cron_task(name:, content:, cron:, enabled: true) write_task(name, content) add_schedule(name: name, task: name, cron: cron, enabled: enabled) end |
#delete_cron_task(name) ⇒ Object
Delete a cron-task: remove both the task file and its schedule.
122 123 124 125 126 |
# File 'lib/clacky/server/scheduler.rb', line 122 def delete_cron_task(name) removed_schedule = remove_schedule(name) removed_task = delete_task(name) removed_schedule || removed_task end |
#delete_task(task_name) ⇒ Object
Delete a task file and remove all schedules that reference it. Returns true if the task file existed and was deleted, false otherwise.
174 175 176 177 178 179 180 181 182 183 184 |
# File 'lib/clacky/server/scheduler.rb', line 174 def delete_task(task_name) path = task_file_path(task_name) return false unless File.exist?(path) File.delete(path) # Remove all schedules referencing this task load_schedules.select { |s| s["task"] == task_name }.each do |s| remove_schedule(s["name"]) end true end |
#list_cron_tasks ⇒ Object
Return a merged list of cron-tasks (task content + schedule metadata).
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 |
# File 'lib/clacky/server/scheduler.rb', line 129 def list_cron_tasks schedule_map = load_schedules.each_with_object({}) do |s, h| h[s["task"]] = s if s.is_a?(Hash) end list_tasks.map do |task_name| content = begin; read_task(task_name); rescue StandardError; ""; end schedule = schedule_map[task_name] || {} { "name" => task_name, "content" => content, "cron" => schedule["cron"], "enabled" => schedule.fetch("enabled", true), "scheduled" => !schedule.empty? } end end |
#list_tasks ⇒ Object
List all existing task names.
164 165 166 167 168 169 170 |
# File 'lib/clacky/server/scheduler.rb', line 164 def list_tasks return [] unless Dir.exist?(TASKS_DIR) Dir.glob(File.join(TASKS_DIR, "*.md")).map do |path| File.basename(path, ".md") end.sort end |
#read_task(task_name) ⇒ Object
Read the prompt content of a named task.
150 151 152 153 154 155 |
# File 'lib/clacky/server/scheduler.rb', line 150 def read_task(task_name) path = task_file_path(task_name) raise "Task not found: #{task_name} (expected #{path})" unless File.exist?(path) File.read(path) end |
#remove_schedule(name) ⇒ Object
Remove a schedule entry by name.
84 85 86 87 88 89 90 |
# File 'lib/clacky/server/scheduler.rb', line 84 def remove_schedule(name) list = load_schedules before_count = list.size list.reject! { |s| s["name"] == name } save_schedules(list) list.size < before_count end |
#running? ⇒ Boolean
58 59 60 |
# File 'lib/clacky/server/scheduler.rb', line 58 def running? @running end |
#schedules ⇒ Object
Return all schedules from the config file.
63 64 65 |
# File 'lib/clacky/server/scheduler.rb', line 63 def schedules load_schedules end |
#start ⇒ Object
Start the background scheduler thread.
39 40 41 42 43 44 45 46 47 |
# File 'lib/clacky/server/scheduler.rb', line 39 def start @mutex.synchronize do return if @running @running = true @thread = Thread.new { run_loop } @thread.name = "clacky-scheduler" end end |
#stop ⇒ Object
Stop the background scheduler thread gracefully. NOTE: intentionally avoids Mutex here so it is safe to call from a signal trap context (Ruby disallows Mutex#synchronize inside traps).
52 53 54 55 56 |
# File 'lib/clacky/server/scheduler.rb', line 52 def stop @running = false @thread&.wakeup rescue nil @thread&.join(5) end |
#task_file_path(task_name) ⇒ Object
Return the file path for a task.
187 188 189 |
# File 'lib/clacky/server/scheduler.rb', line 187 def task_file_path(task_name) File.join(TASKS_DIR, "#{task_name}.md") end |
#update_cron_task(name, content: nil, cron: nil, enabled: nil) ⇒ Object
Update a cron-task: optionally update content and/or schedule fields.
114 115 116 117 118 119 |
# File 'lib/clacky/server/scheduler.rb', line 114 def update_cron_task(name, content: nil, cron: nil, enabled: nil) raise "Cron task not found: #{name}" unless list_tasks.include?(name) write_task(name, content) unless content.nil? update_schedule(name, cron: cron, enabled: enabled) if cron || !enabled.nil? end |
#update_schedule(name, cron: nil, enabled: nil) ⇒ Object
Update an existing schedule entry (cron and/or enabled). Returns false if the schedule does not exist.
94 95 96 97 98 99 100 101 102 103 |
# File 'lib/clacky/server/scheduler.rb', line 94 def update_schedule(name, cron: nil, enabled: nil) list = load_schedules entry = list.find { |s| s["name"] == name } return false unless entry entry["cron"] = cron unless cron.nil? entry["enabled"] = enabled unless enabled.nil? save_schedules(list) true end |
#write_task(task_name, content) ⇒ Object
Write the prompt content for a named task.
158 159 160 161 |
# File 'lib/clacky/server/scheduler.rb', line 158 def write_task(task_name, content) FileUtils.mkdir_p(TASKS_DIR) File.write(task_file_path(task_name), content) end |