feat: ship agent-first mission control v0.2.57
This commit is contained in:
@@ -0,0 +1,134 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Nexus.Api.Data;
|
||||
|
||||
namespace Nexus.Api.Repositories;
|
||||
|
||||
public sealed class OpenClawConnectionProfileRepository(NexusDbContext db)
|
||||
: IOpenClawConnectionProfileRepository
|
||||
{
|
||||
public Task<OpenClawConnectionProfile?> GetPrimaryAsync(
|
||||
CancellationToken cancellationToken = default)
|
||||
=> db.Set<OpenClawConnectionProfile>()
|
||||
.AsNoTracking()
|
||||
.SingleOrDefaultAsync(
|
||||
profile => profile.ProfileId == OpenClawConnectionProfile.PrimaryProfileId,
|
||||
cancellationToken);
|
||||
|
||||
public async Task<OpenClawConnectionProfile> SavePrimaryAsync(
|
||||
OpenClawConnectionProfile profile,
|
||||
int? expectedRevision,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(profile);
|
||||
if (!string.Equals(
|
||||
profile.ProfileId,
|
||||
OpenClawConnectionProfile.PrimaryProfileId,
|
||||
StringComparison.Ordinal))
|
||||
{
|
||||
throw new ArgumentException("Only the primary OpenClaw profile is supported.", nameof(profile));
|
||||
}
|
||||
|
||||
var profiles = db.Set<OpenClawConnectionProfile>();
|
||||
var current = await profiles.SingleOrDefaultAsync(
|
||||
item => item.ProfileId == OpenClawConnectionProfile.PrimaryProfileId,
|
||||
cancellationToken);
|
||||
|
||||
if (current is null)
|
||||
{
|
||||
if (expectedRevision is not null and not 0)
|
||||
{
|
||||
throw new OpenClawConnectionProfileConcurrencyException(
|
||||
"The OpenClaw connection profile no longer exists.");
|
||||
}
|
||||
|
||||
profile.Revision = 1;
|
||||
profile.CreatedAt = profile.CreatedAt == default
|
||||
? DateTimeOffset.UtcNow
|
||||
: profile.CreatedAt;
|
||||
profile.UpdatedAt = DateTimeOffset.UtcNow;
|
||||
profiles.Add(profile);
|
||||
await SaveChangesAsync(cancellationToken);
|
||||
return Clone(profile);
|
||||
}
|
||||
|
||||
if (expectedRevision is null || expectedRevision.Value != current.Revision)
|
||||
{
|
||||
throw new OpenClawConnectionProfileConcurrencyException(
|
||||
"The OpenClaw connection profile changed since it was loaded.",
|
||||
current.Revision);
|
||||
}
|
||||
|
||||
current.Endpoint = profile.Endpoint;
|
||||
current.DiscoverySource = profile.DiscoverySource;
|
||||
current.RequiredVersion = profile.RequiredVersion;
|
||||
current.TlsCertificateFingerprint = profile.TlsCertificateFingerprint;
|
||||
current.AdoptionState = profile.AdoptionState;
|
||||
current.ManagementEnabled = profile.ManagementEnabled;
|
||||
current.CapabilityHash = profile.CapabilityHash;
|
||||
current.DeviceId = profile.DeviceId;
|
||||
current.LastProbedAt = profile.LastProbedAt;
|
||||
current.LastVerifiedAt = profile.LastVerifiedAt;
|
||||
current.AdoptedAt = profile.AdoptedAt;
|
||||
current.UpdatedAt = DateTimeOffset.UtcNow;
|
||||
current.Revision++;
|
||||
|
||||
await SaveChangesAsync(cancellationToken);
|
||||
return Clone(current);
|
||||
}
|
||||
|
||||
public async Task<bool> DeletePrimaryAsync(
|
||||
int expectedRevision,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var profiles = db.Set<OpenClawConnectionProfile>();
|
||||
var current = await profiles.SingleOrDefaultAsync(
|
||||
item => item.ProfileId == OpenClawConnectionProfile.PrimaryProfileId,
|
||||
cancellationToken);
|
||||
if (current is null)
|
||||
return false;
|
||||
if (current.Revision != expectedRevision)
|
||||
{
|
||||
throw new OpenClawConnectionProfileConcurrencyException(
|
||||
"The OpenClaw connection profile changed since it was loaded.",
|
||||
current.Revision);
|
||||
}
|
||||
|
||||
profiles.Remove(current);
|
||||
await SaveChangesAsync(cancellationToken);
|
||||
return true;
|
||||
}
|
||||
|
||||
private async Task SaveChangesAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
await db.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
catch (DbUpdateConcurrencyException exception)
|
||||
{
|
||||
throw new OpenClawConnectionProfileConcurrencyException(
|
||||
"The OpenClaw connection profile changed concurrently.",
|
||||
innerException: exception);
|
||||
}
|
||||
}
|
||||
|
||||
private static OpenClawConnectionProfile Clone(OpenClawConnectionProfile profile)
|
||||
=> new()
|
||||
{
|
||||
ProfileId = profile.ProfileId,
|
||||
Endpoint = profile.Endpoint,
|
||||
DiscoverySource = profile.DiscoverySource,
|
||||
RequiredVersion = profile.RequiredVersion,
|
||||
TlsCertificateFingerprint = profile.TlsCertificateFingerprint,
|
||||
AdoptionState = profile.AdoptionState,
|
||||
ManagementEnabled = profile.ManagementEnabled,
|
||||
CapabilityHash = profile.CapabilityHash,
|
||||
DeviceId = profile.DeviceId,
|
||||
Revision = profile.Revision,
|
||||
CreatedAt = profile.CreatedAt,
|
||||
UpdatedAt = profile.UpdatedAt,
|
||||
LastProbedAt = profile.LastProbedAt,
|
||||
LastVerifiedAt = profile.LastVerifiedAt,
|
||||
AdoptedAt = profile.AdoptedAt
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user