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

@@ -64,31 +64,37 @@ public sealed class AgentHarness : IAsyncDisposable
LoadedNodeIdentity identity = LoadedNodeIdentity.Load(directory);
var app = new AgentApp(config, identity, directory, TimeProvider.System, NullNodeLog.Instance);
var harness = new AgentHarness(backend, directory, identity, app);
await app.Ready.WaitAsync(TimeSpan.FromSeconds(10));
await Task.WhenAny(app.Ready, harness._run).WaitAsync(TimeSpan.FromSeconds(10));
if (harness._run.IsCompleted)
{
await harness._run;
}
await app.Ready;
await BackendFixture.EventuallyAsync(() => Task.FromResult(app.Link.IsConnected), because: "the agent should connect to the backend");
return harness;
}
/// <summary>注册一个 Probe(密钥由它自己生成),并构造连到这个 Agent 的假 Probe:对等面用它的对等面密钥。</summary>
public async Task<ProbeSimulator> CreateProbeAsync()
/// <summary>注册一个拨号侧 Agent(密钥由它自己生成),并构造连到这个 Agent 的拨号侧模拟器:对等面用它的对等面密钥。</summary>
public async Task<DialerSimulator> CreateDialerAsync()
{
using EnrolledProbe probe = await Backend.CreateProbeAsync();
// 假 Probe 要在注册目录被释放之后继续用这把密钥:导出再导入一份
NodeIdentity key = NodeIdentity.FromPkcs8(probe.Identity.PeerIdentity.ExportPkcs8());
using EnrolledDialer dialer = await Backend.CreateDialerAsync();
// 拨号侧模拟器 要在注册目录被释放之后继续用这把密钥:导出再导入一份
NodeIdentity key = NodeIdentity.FromPkcs8(dialer.Identity.PeerIdentity.ExportPkcs8());
string publicKey = Convert.ToBase64String(key.PublicKeySpki);
// 等 Agent 的期望状态里有了这把公钥(否则握手会被拒绝)
await BackendFixture.EventuallyAsync(
() => Task.FromResult(App.IsPeerKnown(probe.ProbeId, publicKey)),
because: "the agent should learn the probe's peer key");
return new ProbeSimulator(probe.ProbeId, key);
() => Task.FromResult(App.IsPeerKnown(dialer.DialerId, publicKey)),
because: "the agent should learn the dialer's peer key");
return new DialerSimulator(dialer.DialerId, key);
}
public async Task<ProbeSimulator> ConnectProbeAsync()
public async Task<DialerSimulator> ConnectDialerAsync()
{
ProbeSimulator probe = await CreateProbeAsync();
await probe.ConnectAsync("127.0.0.1", App.ControlPort, TlsPublicKey, App.QuicPorts[0], NodeId);
return probe;
DialerSimulator dialer = await CreateDialerAsync();
await dialer.ConnectAsync("127.0.0.1", App.ControlPort, TlsPublicKey, App.QuicPorts[0], NodeId);
return dialer;
}
/// <summary>为这个 Agent 创建覆盖任务,让线路任务使用它实际监听的端口(默认模板里是 7443/7444,不在本地允许的端口里)。</summary>