96 lines
3.3 KiB
C#
96 lines
3.3 KiB
C#
using System.Globalization;
|
||
using System.Text.Json;
|
||
using System.Text.Json.Serialization;
|
||
using Fleet.Core;
|
||
|
||
namespace MoShiro.Protocol;
|
||
|
||
/// <summary>Task = { id, scope, kind, target, params, schedule, enabled }。参数已经按 ParamSpec 校验并补全默认值。</summary>
|
||
public sealed class TaskSpec
|
||
{
|
||
[JsonPropertyName("id")]
|
||
public TaskId Id { get; set; }
|
||
|
||
/// <summary>line | target</summary>
|
||
[JsonPropertyName("scope")]
|
||
public string Scope { get; set; } = "line";
|
||
|
||
[JsonPropertyName("kind")]
|
||
public string Kind { get; set; } = "";
|
||
|
||
[JsonPropertyName("target")]
|
||
public string? Target { get; set; }
|
||
|
||
[JsonPropertyName("params")]
|
||
public Dictionary<string, JsonElement> Params { get; set; } = [];
|
||
|
||
/// <summary>continuous | every:<分钟> | once</summary>
|
||
[JsonPropertyName("schedule")]
|
||
public string Schedule { get; set; } = TaskSchedule.Continuous;
|
||
|
||
[JsonPropertyName("enabled")]
|
||
public bool Enabled { get; set; } = true;
|
||
}
|
||
|
||
public sealed class PeerSpec
|
||
{
|
||
[JsonPropertyName("probe_id")]
|
||
public ProbeId ProbeId { get; set; }
|
||
|
||
/// <summary>Probe 的身份公钥(SPKI,base64)。</summary>
|
||
[JsonPropertyName("pubkey")]
|
||
public string PublicKey { get; set; } = "";
|
||
|
||
/// <summary>操作员批准这个 Probe 的证明(passkey 签的委托加码的私钥签的绑定)。固定了创世的 Agent 没有它就不接纳。</summary>
|
||
[JsonPropertyName("proof")]
|
||
public MemberProofDto? Proof { get; set; }
|
||
}
|
||
|
||
/// <summary>期望状态文档(Fleet state type = <c>task_set</c>):Backend 为每个 Agent 计算的有效任务集。</summary>
|
||
public sealed class TaskSet
|
||
{
|
||
public const string StateType = "task_set";
|
||
|
||
[JsonPropertyName("version")]
|
||
public long Version { get; set; }
|
||
|
||
[JsonPropertyName("tasks")]
|
||
public List<TaskSpec> Tasks { get; set; } = [];
|
||
|
||
/// <summary>允许连接到这个 Agent 的 Probe 公钥。</summary>
|
||
[JsonPropertyName("peers")]
|
||
public List<PeerSpec> Peers { get; set; } = [];
|
||
|
||
/// <summary>操作员集合的链(passkey 签的,创世在前):节点从固定的创世指纹验过才采用。</summary>
|
||
[JsonPropertyName("trust_chain")]
|
||
public List<SignedStatementDto>? TrustChain { get; set; }
|
||
}
|
||
|
||
public static class TaskSchedule
|
||
{
|
||
public const string Continuous = "continuous";
|
||
public const string Once = "once";
|
||
public const string EveryPrefix = "every:";
|
||
|
||
public static string Every(int minutes) => EveryPrefix + minutes.ToString(CultureInfo.InvariantCulture);
|
||
|
||
/// <summary>校验调度字符串:<c>every:N</c> 的 N ≥ 60(适用于重负载 Kind)。</summary>
|
||
public static Result<int?> Validate(string schedule)
|
||
{
|
||
if (schedule is Continuous or Once)
|
||
{
|
||
return Result<int?>.Ok(null);
|
||
}
|
||
|
||
if (schedule.StartsWith(EveryPrefix, StringComparison.Ordinal)
|
||
&& int.TryParse(schedule.AsSpan(EveryPrefix.Length), NumberStyles.None, CultureInfo.InvariantCulture, out int minutes))
|
||
{
|
||
return minutes >= 60
|
||
? Result<int?>.Ok(minutes)
|
||
: Result<int?>.Fail(MoShiroErrors.ValidationFailed, "every:N requires N >= 60 minutes");
|
||
}
|
||
|
||
return Result<int?>.Fail(MoShiroErrors.ValidationFailed, "schedule must be continuous, once or every:<minutes>");
|
||
}
|
||
}
|