agent: unify listening and dialing with directed mutual measurements

Replace Probe with Agent across node runtime, Fleet contracts, backend state, API and storage. Report incoming sessions and outgoing link health through each Agent uplink, and retain directed link controls and retry.

Update protocol fixtures and end-to-end coverage for the unified model; accept requested cancellation during the Fleet timeout test cleanup.
This commit is contained in:
justme committed 2026-10-21 11:55:36 +08:00
1 parent d5ebf29e57
commit ad5efc62ff
206 files changed
+4202 -4964

No files matched your search

+16 -16
View File
@@ -4,22 +4,22 @@ using Fleet.Core;
namespace MoShiro.Protocol;
/// <summary>Agent 单调时钟与 UTC 的锚点,以及 Probe 时钟相对 Agent 时钟的偏移估计。</summary>
/// <summary>监听方单调时钟与 UTC 的锚点,以及拨号方时钟相对监听方时钟的偏移估计。</summary>
public sealed class ChunkClock
{
[JsonPropertyName("agent_mono_us")]
public long AgentMonoUs { get; set; }
[JsonPropertyName("listener_mono_us")]
public long ListenerMonoUs { get; set; }
[JsonPropertyName("utc_ms")]
public long UtcMs { get; set; }
/// <summary>Agent 时钟 − Probe 时钟(微秒)。</summary>
[JsonPropertyName("probe_offset_us")]
public long ProbeOffsetUs { get; set; }
/// <summary>监听方时钟 − 拨号方时钟(微秒)。</summary>
[JsonPropertyName("dialer_offset_us")]
public long DialerOffsetUs { get; set; }
/// <summary>Probe 相对 Agent 的时钟漂移(ppb,十亿分之一)。</summary>
[JsonPropertyName("probe_drift_ppb")]
public long ProbeDriftPpb { get; set; }
/// <summary>拨号方相对监听方的时钟漂移(ppb,十亿分之一)。</summary>
[JsonPropertyName("dialer_drift_ppb")]
public long DialerDriftPpb { get; set; }
}
public sealed class ColumnHeader
@@ -38,7 +38,7 @@ public sealed class ColumnHeader
[JsonPropertyName("nullable")]
public bool Nullable { get; set; }
/// <summary>ts_probe | ts_agent | ts_local,其他为空。</summary>
/// <summary>ts_dialer | ts_listener | ts_local,其他为空。</summary>
[JsonPropertyName("role")]
public string? Role { get; set; }
}
@@ -83,8 +83,8 @@ public sealed class RawChunkHeader
[JsonPropertyName("agent_id")]
public NodeId AgentId { get; set; }
[JsonPropertyName("probe_id")]
public ProbeId? ProbeId { get; set; }
[JsonPropertyName("dialer_id")]
public NodeId? DialerId { get; set; }
[JsonPropertyName("target")]
public string? Target { get; set; }
@@ -105,7 +105,7 @@ public sealed class RawChunkHeader
[JsonPropertyName("duration_ms")]
public int DurationMs { get; set; }
/// <summary>Probe 一侧的列没有在宽限期内送达:只有 Agent 一侧的数据。</summary>
/// <summary>拨号方一侧的列没有在宽限期内送达:只有监听方一侧的数据。</summary>
[JsonPropertyName("partial")]
public bool Partial { get; set; }
@@ -116,11 +116,11 @@ public sealed class RawChunkHeader
public List<StreamHeader> Streams { get; set; } = [];
/// <summary>
/// "line:&lt;probe_id&gt;" 或 "target:&lt;task_id&gt;":存储和分析用的序列标识。
/// 同一个 Agent 上每个 Probe 的每种地址族各是一个序列,IPv6 追加 :ipv6,互不混合。
/// "line:&lt;dialer_id&gt;" 或 "target:&lt;task_id&gt;":存储和分析用的序列标识。
/// 同一个监听节点上每个拨号方的每种地址族各是一个序列,IPv6 追加 :ipv6,互不混合。
/// </summary>
[JsonIgnore]
public string Series => TaskId is { } id && Target is not null ? "target:" + id.Value : IpVersions.LineSeries(ProbeId?.Value ?? "", IpVersion);
public string Series => TaskId is { } id && Target is not null ? "target:" + id.Value : IpVersions.LineSeries(DialerId?.Value ?? "", IpVersion);
public const string LinePrefix = "line:";
}
@@ -6,7 +6,7 @@ using Fleet.Core;
namespace MoShiro.Protocol;
/// <summary>
/// RawChunk 的编解码(Probe、Agent、Backend、离线工具共用同一份代码)。
/// RawChunk 的编解码(拨号方、Agent、Backend、离线工具共用同一份代码)。
/// 布局:<c>"MSRC" | u8 container_version | u32 crc32c(压缩数据) | Brotli( u32 header_len | header JSON | 各列数据 )</c>。
/// 列数据:可空列先写有效位图,再写有效值;plain 为无符号 varint(有符号类型用 zigzag),delta 为与上一个有效值之差的 zigzag varint。
/// </summary>
@@ -334,16 +334,16 @@ public static class RawChunkCodec
private static string? RoleName(ColumnRole role) => role switch
{
ColumnRole.TsProbe => "ts_probe",
ColumnRole.TsAgent => "ts_agent",
ColumnRole.TsDialer => "ts_dialer",
ColumnRole.TsListener => "ts_listener",
ColumnRole.TsLocal => "ts_local",
_ => null,
};
private static ColumnRole ParseRole(string? text) => text switch
{
"ts_probe" => ColumnRole.TsProbe,
"ts_agent" => ColumnRole.TsAgent,
"ts_dialer" => ColumnRole.TsDialer,
"ts_listener" => ColumnRole.TsListener,
"ts_local" => ColumnRole.TsLocal,
_ => ColumnRole.None,
};