using System.Text.Json;
using System.Text.Json.Serialization;
using Fleet.Core;
namespace MoShiro.Protocol;
/// moshiro.* 方法名。新增方法 = 在这里定义名称和 params/result,再在组装处加一行 .On(...)。
public static class MoShiroMethods
{
public const string TestRun = "moshiro.test.run";
public const string TestCancel = "moshiro.test.cancel";
public const string ProbeDisconnect = "moshiro.probe.disconnect";
public const string TestStatus = "moshiro.test.status";
public const string Event = "moshiro.event";
public const string Host = "moshiro.host";
public const string UpdateOffer = "moshiro.update.offer";
public const string UpdateApply = "moshiro.update.apply";
// Probe ↔ Agent(点对点会话)
public const string Clock = "moshiro.clock";
public const string TestPhase = "moshiro.test.phase";
public const string TestPhaseEnd = "moshiro.test.phase_end";
public const string TestAbort = "moshiro.test.abort";
}
public static class AgentEventKinds
{
public const string ProbeConnected = "probe_connected";
public const string ProbeDisconnected = "probe_disconnected";
public const string EndpointChanged = "endpoint_changed";
public const string SessionRejected = "session_rejected";
public const string ClockStep = "clock_step";
public const string Gap = "gap";
public const string CpuSaturated = "cpu_saturated";
public static readonly IReadOnlyList All =
[ProbeConnected, ProbeDisconnected, EndpointChanged, SessionRejected, ClockStep, Gap, CpuSaturated];
}
public static class TestStatuses
{
public const string Queued = "queued";
public const string Running = "running";
public const string PhaseDone = "phase_done";
public const string Succeeded = "succeeded";
public const string Failed = "failed";
public const string Aborted = "aborted";
}
public sealed class TestPhase
{
[JsonPropertyName("kind")]
public string Kind { get; set; } = "";
[JsonPropertyName("params")]
public Dictionary Params { get; set; } = [];
[JsonPropertyName("duration_s")]
public int DurationSeconds { get; set; }
}
public sealed class TestRunParams
{
[JsonPropertyName("test_id")]
public TestId TestId { get; set; }
/// 在哪个 Probe 上运行:必须是当前连着这个 Agent 的 Probe。
[JsonPropertyName("probe_id")]
public ProbeId ProbeId { get; set; }
[JsonPropertyName("plan")]
public string Plan { get; set; } = "";
[JsonPropertyName("phases")]
public List Phases { get; set; } = [];
}
public sealed class TestRunResult
{
[JsonPropertyName("accepted")]
public bool Accepted { get; set; }
}
public sealed class TestCancelParams
{
[JsonPropertyName("test_id")]
public TestId TestId { get; set; }
}
/// Backend → Agent:断开某个 Probe 的会话(Probe 会按退避重连,除非它已经被吊销)。
public sealed class ProbeDisconnectParams
{
[JsonPropertyName("probe_id")]
public ProbeId ProbeId { get; set; }
}
public sealed class TestStatusParams
{
[JsonPropertyName("test_id")]
public TestId TestId { get; set; }
[JsonPropertyName("status")]
public string Status { get; set; } = "";
[JsonPropertyName("phase")]
public int? Phase { get; set; }
[JsonPropertyName("error_code")]
public string? ErrorCode { get; set; }
}
public sealed class AgentEventParams
{
/// probe_connected 时:这个 Probe 的 id、版本、地址和连接时间(Backend 不用等下一次心跳就能显示)。
[JsonPropertyName("probe")]
public ConnectedProbe? Probe { get; set; }
[JsonPropertyName("kind")]
public string Kind { get; set; } = "";
[JsonPropertyName("at_utc_ms")]
public long AtUtcMs { get; set; }
[JsonPropertyName("detail")]
public string? Detail { get; set; }
}
public sealed class AgentEndpoint
{
[JsonPropertyName("agent_id")]
public NodeId AgentId { get; set; }
[JsonPropertyName("name")]
public string Name { get; set; } = "";
[JsonPropertyName("host")]
public string Host { get; set; } = "";
[JsonPropertyName("tcp_port")]
public int TcpPort { get; set; }
[JsonPropertyName("udp_ports")]
public List UdpPorts { get; set; } = [];
/// Agent 面向 Probe 的 TLS 证书指纹(SHA-256,base64)。
[JsonPropertyName("spki")]
public string Spki { get; set; } = "";
}
/// 期望状态文档(Fleet state type = probe_agents):Backend 直接下发给每个 Probe 的 Agent 列表。
public sealed class ProbeAgentList
{
public const string StateType = "probe_agents";
[JsonPropertyName("version")]
public long Version { get; set; }
[JsonPropertyName("agents")]
public List Agents { get; set; } = [];
}
/// 注册元数据(MoShiro 定义,经 Fleet 的 meta 字段带上来)。
public sealed class AgentEnrollMeta
{
[JsonPropertyName("probe_tcp_port")]
public int ProbeTcpPort { get; set; } = 7443;
[JsonPropertyName("probe_udp_ports")]
public List ProbeUdpPorts { get; set; } = [7443];
[JsonPropertyName("public_host")]
public string? PublicHost { get; set; }
[JsonPropertyName("kinds")]
public List Kinds { get; set; } = [];
}
public sealed class LocalLimits
{
[JsonPropertyName("max_total_mbps")]
public double MaxTotalMbps { get; set; }
[JsonPropertyName("max_total_pps")]
public long MaxTotalPps { get; set; }
[JsonPropertyName("allowed_target_cidrs")]
public List AllowedTargetCidrs { get; set; } = [];
[JsonPropertyName("max_concurrent_tests")]
public int MaxConcurrentTests { get; set; } = 1;
}
///
/// 节点的系统和架构(粗粒度,只用于展示):os 是 linux / windows / macos / other,name 是「Debian 12」「Windows 11」这样的名字,
/// arch 是 x64 / arm64 / arm / x86。没有主机名、内核版本、具体型号。另外带着这个程序自己的构建信息(版本、tag、构建时间)。
///
public sealed class PlatformInfo
{
[JsonPropertyName("os")]
public string Os { get; set; } = "other";
[JsonPropertyName("name")]
public string? Name { get; set; }
[JsonPropertyName("arch")]
public string? Arch { get; set; }
/// 程序的版本、tag、提交(短)和构建时间(UTC,ISO 8601): 构建时写进去的,没设置就是空。
[JsonPropertyName("version")]
public string? Version { get; set; }
[JsonPropertyName("tag")]
public string? Tag { get; set; }
[JsonPropertyName("commit")]
public string? Commit { get; set; }
[JsonPropertyName("built_at")]
public string? BuiltAt { get; set; }
}
///
/// 面板给节点的更新清单(moshiro.update.offer):manifest 是发布清单的原始文本,signature 是 base64 的签名。
/// 节点只是把它们原样放进数据目录;验签、拉取、重启都由宿主机上的更新器完成(节点进程不执行任何命令,也不信任清单的内容)。
///
public sealed class UpdateOfferParams
{
[JsonPropertyName("manifest")]
public string Manifest { get; set; } = "";
[JsonPropertyName("signature")]
public string Signature { get; set; } = "";
}
/// 宿主机更新器已经验证过的可用更新。
public sealed class UpdateAvailable
{
[JsonPropertyName("version")]
public string? Version { get; set; }
[JsonPropertyName("tag")]
public string? Tag { get; set; }
[JsonPropertyName("digest")]
public string? Digest { get; set; }
[JsonPropertyName("seq")]
public long Seq { get; set; }
}
///
/// 宿主机更新器的状态(它写在 update-status/status.json,节点读出来随心跳上报):
/// up_to_date、available、applying、failed、rolled_back、invalid(清单验签或校验没过)。
/// 没有安装更新器的节点没有这个字段。
///
public sealed class UpdateStatus
{
[JsonPropertyName("state")]
public string State { get; set; } = "";
/// 正在运行的镜像 digest(更新器记录的;还没更新过时为空)。
[JsonPropertyName("current")]
public string? Current { get; set; }
[JsonPropertyName("available")]
public UpdateAvailable? Available { get; set; }
[JsonPropertyName("error")]
public string? Error { get; set; }
[JsonPropertyName("at")]
public long At { get; set; }
}
/// Probe 的 fleet.heartbeat 附加字段:更新器的状态。
public sealed class ProbeHeartbeatExtra
{
[JsonPropertyName("update")]
public UpdateStatus? Update { get; set; }
}
/// Probe 的 fleet.hello 附加字段:系统、架构和构建信息(只用于展示和更新)。
public sealed class ProbeHelloMeta
{
[JsonPropertyName("platform")]
public PlatformInfo? Platform { get; set; }
}
/// fleet.hello 的附加字段。
public sealed class AgentHelloMeta
{
[JsonPropertyName("platform")]
public PlatformInfo? Platform { get; set; }
[JsonPropertyName("tls_spki")]
public string? TlsSpki { get; set; }
[JsonPropertyName("probe_tcp_port")]
public int? ProbeTcpPort { get; set; }
[JsonPropertyName("probe_udp_ports")]
public List? ProbeUdpPorts { get; set; }
[JsonPropertyName("local_limits")]
public LocalLimits? LocalLimits { get; set; }
}
///
/// 主机资源占用的只读快照(Agent → Backend,每隔几秒一次)。只有汇总的数字:没有 CPU 型号、主机名、网卡名,也没有任何 IP 地址。
/// 读不到的项是 null。
///
public sealed class HostSnapshot
{
[JsonPropertyName("cpu_percent")]
public double? CpuPercent { get; set; }
[JsonPropertyName("mem_used")]
public long? MemUsed { get; set; }
[JsonPropertyName("mem_total")]
public long? MemTotal { get; set; }
[JsonPropertyName("disk_used")]
public long? DiskUsed { get; set; }
[JsonPropertyName("disk_total")]
public long? DiskTotal { get; set; }
/// 所有物理网卡的累计收发字节(自开机以来)。
[JsonPropertyName("net_rx_total")]
public long? NetRxTotal { get; set; }
[JsonPropertyName("net_tx_total")]
public long? NetTxTotal { get; set; }
[JsonPropertyName("net_rx_bps")]
public long? NetRxBps { get; set; }
[JsonPropertyName("net_tx_bps")]
public long? NetTxBps { get; set; }
/// 已统计接口的内核报告链路速率之和(Mbit/s);驱动不支持或无法读取时为空,不代表互联网带宽。
[JsonPropertyName("net_link_mbps")]
public long? NetLinkMbps { get; set; }
[JsonPropertyName("uptime_s")]
public long? UptimeSeconds { get; set; }
[JsonPropertyName("at_utc_ms")]
public long AtUtcMs { get; set; }
}
/// Agent 眼中的一个已连接 Probe。
public sealed class ConnectedProbe
{
[JsonPropertyName("id")]
public ProbeId Id { get; set; }
[JsonPropertyName("version")]
public string? Version { get; set; }
/// Probe 的远端地址(经过 NAT 之后的公网 IP:端口)。
[JsonPropertyName("remote")]
public string? Remote { get; set; }
[JsonPropertyName("since_utc_ms")]
public long SinceUtcMs { get; set; }
}
/// fleet.heartbeat 的附加字段:只有进程内的计数,没有主机信息。
public sealed class AgentHeartbeatExtra
{
/// 当前连着的 Probe 会话数(等于 的个数)。
[JsonPropertyName("sessions")]
public int Sessions { get; set; }
/// 当前连着的 Probe:每次心跳都是完整的快照,Backend 靠它纠正错过的 probe_connected / probe_disconnected 事件。
[JsonPropertyName("probes")]
public List Probes { get; set; } = [];
[JsonPropertyName("pps_in")]
public long PpsIn { get; set; }
[JsonPropertyName("pps_out")]
public long PpsOut { get; set; }
[JsonPropertyName("dropped_invalid")]
public long DroppedInvalid { get; set; }
[JsonPropertyName("dropped_rate_limited")]
public long DroppedRateLimited { get; set; }
[JsonPropertyName("queue_bytes")]
public long QueueBytes { get; set; }
[JsonPropertyName("update")]
public UpdateStatus? Update { get; set; }
}
/// Agent 下发给 Probe 的一条线路任务(期望状态类型 line_task_set 里的一项)。
public sealed class LineTask
{
[JsonPropertyName("id")]
public TaskId Id { get; set; }
[JsonPropertyName("kind")]
public string Kind { get; set; } = "";
[JsonPropertyName("params")]
public Dictionary Params { get; set; } = [];
/// 数据面用它区分任务;Agent 在本进程生命周期内分配。
[JsonPropertyName("stream_id")]
public uint StreamId { get; set; }
[JsonPropertyName("port")]
public int Port { get; set; }
}
/// 这条线路的期望状态(Agent → Probe):Probe 按它调和自己一侧的角色。
public sealed class LineTaskSet
{
public const string StateType = "line_task_set";
[JsonPropertyName("version")]
public long Version { get; set; }
[JsonPropertyName("tasks")]
public List Tasks { get; set; } = [];
/// Agent 的数据面端口(Probe 把包发到这里)。
[JsonPropertyName("udp_ports")]
public List UdpPorts { get; set; } = [];
[JsonPropertyName("tcp_ports")]
public List TcpPorts { get; set; } = [];
}
/// moshiro.clock:Probe 用它估计与 Agent 单调时钟的偏移(对称往返假设)。
public sealed class ClockParams
{
[JsonPropertyName("probe_mono_us")]
public long ProbeMonoUs { get; set; }
}
public sealed class ClockResult
{
[JsonPropertyName("agent_mono_us")]
public long AgentMonoUs { get; set; }
}
/// 按需测试的一个阶段(Agent → Probe):Agent 编排时间,Probe 准备好自己一侧的角色之后应答。
public sealed class TestPhaseStartParams
{
[JsonPropertyName("test_id")]
public TestId TestId { get; set; }
[JsonPropertyName("phase")]
public int Phase { get; set; }
[JsonPropertyName("kind")]
public string Kind { get; set; } = "";
[JsonPropertyName("params")]
public Dictionary Params { get; set; } = [];
[JsonPropertyName("stream_id")]
public uint StreamId { get; set; }
[JsonPropertyName("duration_s")]
public int DurationSeconds { get; set; }
[JsonPropertyName("port")]
public int Port { get; set; }
}
public sealed class TestPhaseStartResult
{
[JsonPropertyName("accepted")]
public bool Accepted { get; set; }
}
/// 阶段结束(Agent → Probe):Probe 收尾并把自己一侧的列块交给 Agent。sent_count 是 Agent 一侧发出的包数(单向数据流用)。
public sealed class TestPhaseEndParams
{
[JsonPropertyName("test_id")]
public TestId TestId { get; set; }
[JsonPropertyName("phase")]
public int Phase { get; set; }
[JsonPropertyName("stream_id")]
public uint StreamId { get; set; }
[JsonPropertyName("sent_count")]
public long SentCount { get; set; }
}
/// Probe 收尾之后回复:它交给 Agent 的列块个数(Agent 据此等待全部列块到齐再进入下一阶段)。
public sealed class TestPhaseEndResult
{
[JsonPropertyName("fragments")]
public int Fragments { get; set; }
}
public sealed class TestAbortParams
{
[JsonPropertyName("test_id")]
public TestId TestId { get; set; }
}
/// Probe 一侧的列块放在 RawChunk 头部 params 里的保留键:本块覆盖的序号范围 [from, to)(单向数据流由 Agent 一侧按它取行)。
public static class FragmentKeys
{
public const string SeqFrom = "_seq_from";
public const string SeqTo = "_seq_to";
}
[JsonSourceGenerationOptions(DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)]
[JsonSerializable(typeof(LineTaskSet))]
[JsonSerializable(typeof(ClockParams))]
[JsonSerializable(typeof(ClockResult))]
[JsonSerializable(typeof(TestPhaseStartParams))]
[JsonSerializable(typeof(TestPhaseStartResult))]
[JsonSerializable(typeof(TestPhaseEndParams))]
[JsonSerializable(typeof(TestPhaseEndResult))]
[JsonSerializable(typeof(TestAbortParams))]
[JsonSerializable(typeof(TaskSet))]
[JsonSerializable(typeof(TestRunParams))]
[JsonSerializable(typeof(TestRunResult))]
[JsonSerializable(typeof(TestCancelParams))]
[JsonSerializable(typeof(ProbeDisconnectParams))]
[JsonSerializable(typeof(HostSnapshot))]
[JsonSerializable(typeof(TestStatusParams))]
[JsonSerializable(typeof(AgentEventParams))]
[JsonSerializable(typeof(ProbeAgentList))]
[JsonSerializable(typeof(UpdateOfferParams))]
[JsonSerializable(typeof(UpdateStatus))]
[JsonSerializable(typeof(ProbeHeartbeatExtra))]
[JsonSerializable(typeof(ProbeHelloMeta))]
[JsonSerializable(typeof(AgentEnrollMeta))]
[JsonSerializable(typeof(PlatformInfo))]
[JsonSerializable(typeof(AgentHelloMeta))]
[JsonSerializable(typeof(AgentHeartbeatExtra))]
[JsonSerializable(typeof(RawChunkHeader))]
public sealed partial class MoShiroJsonContext : JsonSerializerContext;