Cut over the window to Explorer.Host.exe so only the host writes the index.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-24 17:39:13 +02:00
parent 7c23bc2474
commit 48d03f794f
26 changed files with 774 additions and 59 deletions

View File

@@ -1,4 +1,5 @@
using System.Diagnostics;
using Explorer.Contracts;
using Explorer.Domain;
using Explorer.Domain.Abstractions;
using Microsoft.Extensions.Logging;
@@ -12,6 +13,7 @@ public sealed class SourceManager
private readonly IAppEnvironment _env;
private readonly IClock _clock;
private readonly ILogger<SourceManager> _logger;
private readonly ISourceHost? _remote;
private readonly object _refreshLock = new();
private Task<IReadOnlyList<Source>>? _refreshInFlight;
@@ -23,18 +25,31 @@ public sealed class SourceManager
IVolumeService volumes,
IAppEnvironment env,
IClock clock,
ILogger<SourceManager> logger)
ILogger<SourceManager> logger,
ISourceHost? remote = null)
{
_store = store;
_volumes = volumes;
_env = env;
_clock = clock;
_logger = logger;
_remote = remote;
}
public async Task InitializeAsync(CancellationToken cancellationToken = default)
{
await _store.OpenAsync(cancellationToken).ConfigureAwait(false);
if (!_store.CanWrite)
{
if (_remote is not null)
{
await _remote.RefreshAsync(cancellationToken).ConfigureAwait(false);
}
InvalidateRefreshCache();
return;
}
await _store.ScanJobs.InterruptRunningAsync(cancellationToken).ConfigureAwait(false);
await _store.Excludes.EnsureDefaultsAsync(DefaultExcludes.Create(), cancellationToken).ConfigureAwait(false);
await RefreshOnlineStateAsync(cancellationToken).ConfigureAwait(false);
@@ -70,6 +85,22 @@ public sealed class SourceManager
private async Task<IReadOnlyList<Source>> RefreshOnlineStateCoreAsync(CancellationToken cancellationToken)
{
if (!_store.CanWrite)
{
if (_remote is not null)
{
await _remote.RefreshAsync(cancellationToken).ConfigureAwait(false);
}
var snapshot = await _store.Sources.GetAllAsync(cancellationToken).ConfigureAwait(false);
lock (_refreshLock)
{
_refreshCacheTimestamp = Stopwatch.GetTimestamp();
}
return snapshot;
}
var started = Stopwatch.GetTimestamp();
var known = (await _store.Sources.GetAllAsync(cancellationToken).ConfigureAwait(false)).ToList();
var online = _volumes.EnumerateOnlineVolumes();
@@ -201,6 +232,18 @@ public sealed class SourceManager
public async Task<Source> AddUncAsync(string path, CancellationToken cancellationToken = default)
{
if (!_store.CanWrite)
{
if (_remote is null)
{
throw new InvalidOperationException("Cannot add a network location while the index is read-only.");
}
var added = await _remote.AddUncAsync(path, cancellationToken).ConfigureAwait(false);
InvalidateRefreshCache();
return added;
}
var root = PathRules.CanonicalUncRoot(path);
var known = await _store.Sources.GetAllAsync(cancellationToken).ConfigureAwait(false);
var fp = new VolumeFingerprint
@@ -269,6 +312,18 @@ public sealed class SourceManager
public async Task<bool> ForgetDisconnectedAsync(string path, CancellationToken cancellationToken = default)
{
if (!_store.CanWrite)
{
if (_remote is null)
{
return false;
}
var forgotten = await _remote.ForgetAsync(path, cancellationToken).ConfigureAwait(false);
InvalidateRefreshCache();
return forgotten;
}
var source = await FindSourceRootAsync(path, cancellationToken).ConfigureAwait(false);
if (source is null || !CanForget(source))
{
@@ -324,6 +379,18 @@ public sealed class SourceManager
return null;
}
if (!_store.CanWrite)
{
if (_remote is null)
{
return await FindByPathAsync(path, cancellationToken).ConfigureAwait(false);
}
var ensured = await _remote.EnsureForPathAsync(path, cancellationToken).ConfigureAwait(false);
InvalidateRefreshCache();
return ensured;
}
var existing = await FindByPathAsync(path, cancellationToken).ConfigureAwait(false);
if (existing is not null)
{