107 lines
4.2 KiB
C#
107 lines
4.2 KiB
C#
using ClawdDotNet.Core.Config;
|
|
using ClawdDotNet.Core.Scheduling;
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
namespace ClawdDotNet.Core.Tasks;
|
|
|
|
/// <summary>
|
|
/// Überführt die alten Scheduler-Konfigurationen ins Taskboard — der letzte Schritt, um
|
|
/// die Alt-Scheduler abzulösen: alles Periodische ist danach ein Task.
|
|
///
|
|
/// - Ein <c>scheduler</c> (Agent nach Cron) → ein Task mit <c>when: cron</c> und Assignee
|
|
/// <c>@new:<agent></c> (frischer Lauf, wie der alte AgentScheduler).
|
|
/// - Jeder <c>toolJobs</c>-Eintrag (Poll) → ein Task vom Typ <c>tool_job</c>.
|
|
///
|
|
/// Einmalig und nicht-destruktiv: Existiert der Task (stabile Id) schon, wird er
|
|
/// übersprungen — spätere Änderungen an der Task-Datei bleiben erhalten.
|
|
/// </summary>
|
|
public sealed class SchedulerTaskMigration
|
|
{
|
|
private readonly TaskboardService _board;
|
|
private readonly ITaskRepository _repo;
|
|
private readonly ILogger _logger;
|
|
|
|
public SchedulerTaskMigration(TaskboardService board, ITaskRepository repo, ILoggerFactory loggerFactory)
|
|
{
|
|
_board = board;
|
|
_repo = repo;
|
|
_logger = loggerFactory.CreateLogger("ClawdDotNet.Core.Tasks.SchedulerMigration");
|
|
}
|
|
|
|
public async Task<int> RunAsync(IEnumerable<AgentConfig> agents, CancellationToken ct)
|
|
{
|
|
// IANA-Schreibweise, nicht TimeZoneInfo.Local.Id: Unter Windows lieferte das
|
|
// "W. Europe Standard Time", unter Linux "Europe/Berlin". Die erzeugten
|
|
// Task-Dateien wandern zwischen Rechnern — sie brauchen die Form, die überall
|
|
// gilt.
|
|
var localTz = TimeZones.LocalIanaId;
|
|
var created = 0;
|
|
|
|
foreach (var agent in agents)
|
|
{
|
|
if (agent.Scheduler is { } scheduler && !string.IsNullOrWhiteSpace(scheduler.Cron))
|
|
created += await MigrateSchedulerAsync(agent, scheduler, localTz, ct);
|
|
|
|
foreach (var job in agent.ToolJobs)
|
|
created += await MigrateToolJobAsync(agent, job, localTz, ct);
|
|
}
|
|
|
|
if (created > 0)
|
|
_logger.LogInformation("Scheduler-Migration: {Count} Task(s) aus Alt-Konfiguration angelegt", created);
|
|
|
|
return created;
|
|
}
|
|
|
|
private async Task<int> MigrateSchedulerAsync(
|
|
AgentConfig agent, SchedulerConfig scheduler, string tz, CancellationToken ct)
|
|
{
|
|
var id = $"sched-{agent.AgentId}";
|
|
if (await _repo.GetAsync(id, ct) is not null)
|
|
return 0;
|
|
|
|
await _board.CreateAsync(new TaskItem
|
|
{
|
|
Id = id,
|
|
Title = $"Geplanter Lauf: {Display(agent)}",
|
|
Body = scheduler.TaskMessage,
|
|
Status = TaskItemStatus.Todo,
|
|
Type = TaskItemType.Work,
|
|
Assignee = $"@new:{agent.AgentId}", // frischer Lauf, wie der alte AgentScheduler
|
|
When = new TaskWhen { Kind = TaskWhenKind.Cron, Value = scheduler.Cron, TimeZone = tz }
|
|
}, ct);
|
|
|
|
return 1;
|
|
}
|
|
|
|
private async Task<int> MigrateToolJobAsync(
|
|
AgentConfig agent, ToolJobConfig job, string tz, CancellationToken ct)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(job.Cron) || string.IsNullOrWhiteSpace(job.ToolName))
|
|
return 0;
|
|
|
|
var id = $"tj-{agent.AgentId}-{job.JobId}";
|
|
if (await _repo.GetAsync(id, ct) is not null)
|
|
return 0;
|
|
|
|
await _board.CreateAsync(new TaskItem
|
|
{
|
|
Id = id,
|
|
Title = $"Poll: {job.ToolName}/{job.JobTypeId} → {Display(agent)}",
|
|
Body = $"Wiederkehrender {job.ToolName}-Poll ({job.JobTypeId}). Weckt {Display(agent)} bei einem Ereignis.",
|
|
// Deaktivierte Jobs kommen als backlog — der Scanner nimmt nur todo/backlog...
|
|
// backlog wird aber nicht geclaimt, also ruht der Poll, bis jemand ihn aktiviert.
|
|
Status = job.Enabled ? TaskItemStatus.Todo : TaskItemStatus.Backlog,
|
|
Type = TaskItemType.ToolJob,
|
|
ToolName = job.ToolName,
|
|
JobTypeId = job.JobTypeId,
|
|
Assignee = $"@{agent.AgentId}", // Zielagent zum Wecken
|
|
When = new TaskWhen { Kind = TaskWhenKind.Cron, Value = job.Cron, TimeZone = tz }
|
|
}, ct);
|
|
|
|
return 1;
|
|
}
|
|
|
|
private static string Display(AgentConfig agent)
|
|
=> string.IsNullOrWhiteSpace(agent.DisplayName) ? agent.AgentId : agent.DisplayName;
|
|
}
|