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.
69 70 71 72 73 74 75 76 77 78 79 80 |
# File 'lib/clacky/server/scheduler.rb', line 69 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.
107 108 109 110 |
# File 'lib/clacky/server/scheduler.rb', line 107 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.
121 122 123 124 125 |
# File 'lib/clacky/server/scheduler.rb', line 121 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.
173 174 175 176 177 178 179 180 181 182 183 |
# File 'lib/clacky/server/scheduler.rb', line 173 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).
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
# File 'lib/clacky/server/scheduler.rb', line 128 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.
163 164 165 166 167 168 169 |
# File 'lib/clacky/server/scheduler.rb', line 163 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.
149 150 151 152 153 154 |
# File 'lib/clacky/server/scheduler.rb', line 149 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.
83 84 85 86 87 88 89 |
# File 'lib/clacky/server/scheduler.rb', line 83 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
57 58 59 |
# File 'lib/clacky/server/scheduler.rb', line 57 def running? @running end |
#schedules ⇒ Object
Return all schedules from the config file.
62 63 64 |
# File 'lib/clacky/server/scheduler.rb', line 62 def schedules load_schedules end |
#start ⇒ Object
Start the background scheduler thread.
39 40 41 42 43 44 45 46 |
# File 'lib/clacky/server/scheduler.rb', line 39 def start @mutex.synchronize do return if @running @running = true @thread = Clacky::ThreadRegistry.spawn(name: "clacky-scheduler") { run_loop } 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).
51 52 53 54 55 |
# File 'lib/clacky/server/scheduler.rb', line 51 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.
186 187 188 |
# File 'lib/clacky/server/scheduler.rb', line 186 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.
113 114 115 116 117 118 |
# File 'lib/clacky/server/scheduler.rb', line 113 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.
93 94 95 96 97 98 99 100 101 102 |
# File 'lib/clacky/server/scheduler.rb', line 93 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.
157 158 159 160 |
# File 'lib/clacky/server/scheduler.rb', line 157 def write_task(task_name, content) FileUtils.mkdir_p(TASKS_DIR) File.write(task_file_path(task_name), content) end |