fix(cron): show schedule details and run state in _list_jobs() output
_list_jobs() only displayed job name, id, and schedule kind (e.g. "cron"), omitting the actual timing and run state. The agent couldn't answer "when does this run?" or "did it run?" even though CronSchedule and CronJobState had all the data. Now surfaces: - Cron expression + timezone for cron jobs - Human-readable interval for every jobs - ISO timestamp for one-shot at jobs - Enabled/disabled status - Last run time + status (ok/error/skipped) + error message - Next scheduled run time Fixes #1496 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -147,7 +147,41 @@ class CronTool(Tool):
|
||||
jobs = self._cron.list_jobs()
|
||||
if not jobs:
|
||||
return "No scheduled jobs."
|
||||
lines = [f"- {j.name} (id: {j.id}, {j.schedule.kind})" for j in jobs]
|
||||
lines = []
|
||||
for j in jobs:
|
||||
s = j.schedule
|
||||
if s.kind == "cron":
|
||||
timing = f"cron: {s.expr}"
|
||||
if s.tz:
|
||||
timing += f" ({s.tz})"
|
||||
elif s.kind == "every" and s.every_ms:
|
||||
secs = s.every_ms // 1000
|
||||
if secs >= 3600:
|
||||
timing = f"every {secs // 3600}h"
|
||||
elif secs >= 60:
|
||||
timing = f"every {secs // 60}m"
|
||||
else:
|
||||
timing = f"every {secs}s"
|
||||
elif s.kind == "at" and s.at_ms:
|
||||
from datetime import datetime, timezone
|
||||
dt = datetime.fromtimestamp(s.at_ms / 1000, tz=timezone.utc)
|
||||
timing = f"at {dt.isoformat()}"
|
||||
else:
|
||||
timing = s.kind
|
||||
status = "enabled" if j.enabled else "disabled"
|
||||
parts = [f"- {j.name} (id: {j.id}, {timing}, {status})"]
|
||||
if j.state.last_run_at_ms:
|
||||
from datetime import datetime, timezone
|
||||
last_dt = datetime.fromtimestamp(j.state.last_run_at_ms / 1000, tz=timezone.utc)
|
||||
last_info = f" Last run: {last_dt.isoformat()} — {j.state.last_status or 'unknown'}"
|
||||
if j.state.last_error:
|
||||
last_info += f" ({j.state.last_error})"
|
||||
parts.append(last_info)
|
||||
if j.state.next_run_at_ms:
|
||||
from datetime import datetime, timezone
|
||||
next_dt = datetime.fromtimestamp(j.state.next_run_at_ms / 1000, tz=timezone.utc)
|
||||
parts.append(f" Next run: {next_dt.isoformat()}")
|
||||
lines.append("\n".join(parts))
|
||||
return "Scheduled jobs:\n" + "\n".join(lines)
|
||||
|
||||
def _remove_job(self, job_id: str | None) -> str:
|
||||
|
||||
Reference in New Issue
Block a user