Add plugin isolation IPC scaffolding and host phase one docs (#5)

This commit is contained in:
lincube
2026-04-22 10:25:46 +08:00
committed by GitHub
parent 9224c9a33a
commit f51ec309a6
42 changed files with 1281 additions and 4 deletions

View File

@@ -0,0 +1,12 @@
using Microsoft.Extensions.DependencyInjection;
namespace LanMountainDesktop.PluginSdk;
public interface IPluginWorker
{
void ConfigureServices(IPluginWorkerContext context, IServiceCollection services);
Task StartAsync(IPluginWorkerContext context, IServiceProvider services, CancellationToken cancellationToken = default);
Task StopAsync(CancellationToken cancellationToken = default);
}

View File

@@ -0,0 +1,26 @@
using LanMountainDesktop.PluginIsolation.Contracts;
namespace LanMountainDesktop.PluginSdk;
public interface IPluginWorkerContext
{
string PluginId { get; }
PluginManifest Manifest { get; }
PluginRuntimeMode RuntimeMode { get; }
string SessionId { get; }
string HostPipeName { get; }
string ProtocolVersion { get; }
string PluginDirectory { get; }
string DataDirectory { get; }
IReadOnlyList<PluginCapabilityDeclaration> GrantedCapabilities { get; }
IReadOnlyDictionary<string, string> StartupProperties { get; }
}

View File

@@ -25,6 +25,7 @@
<PackageReference Include="FluentIcons.Avalonia.Fluent" Version="2.0.320" ExcludeAssets="runtime" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="10.0.0" />
<ProjectReference Include="..\LanMountainDesktop.PluginIsolation.Contracts\LanMountainDesktop.PluginIsolation.Contracts.csproj" />
<ProjectReference Include="..\LanMountainDesktop.Shared.Contracts\LanMountainDesktop.Shared.Contracts.csproj" />
</ItemGroup>

View File

@@ -10,7 +10,8 @@ public sealed record PluginManifest(
string? Author = null,
string? Version = null,
string? ApiVersion = null,
IReadOnlyList<PluginSharedContractReference>? SharedContracts = null)
IReadOnlyList<PluginSharedContractReference>? SharedContracts = null,
PluginRuntimeConfiguration? Runtime = null)
{
private static readonly JsonSerializerOptions SerializerOptions = new()
{
@@ -56,9 +57,13 @@ public sealed record PluginManifest(
return Path.GetFullPath(Path.Combine(manifestDirectory, EntranceAssembly));
}
public PluginRuntimeMode RuntimeMode =>
PluginRuntimeModes.TryParse(Runtime?.Mode, out var mode) ? mode : PluginRuntimeMode.InProcess;
private PluginManifest NormalizeAndValidate(string manifestPath)
{
var normalizedSharedContracts = NormalizeSharedContracts(manifestPath, SharedContracts);
var normalizedRuntime = (Runtime ?? new PluginRuntimeConfiguration()).NormalizeAndValidate(manifestPath);
var normalized = this with
{
Id = RequireValue(Id, nameof(Id), manifestPath),
@@ -68,7 +73,8 @@ public sealed record PluginManifest(
Author = NormalizeOptionalValue(Author),
Version = NormalizeOptionalValue(Version),
ApiVersion = NormalizeOptionalValue(ApiVersion) ?? PluginSdkInfo.ApiVersion,
SharedContracts = normalizedSharedContracts
SharedContracts = normalizedSharedContracts,
Runtime = normalizedRuntime
};
if (!System.Version.TryParse(normalized.ApiVersion, out var requestedVersion))

View File

@@ -0,0 +1,15 @@
namespace LanMountainDesktop.PluginSdk;
public sealed record PluginRuntimeConfiguration(string Mode = PluginRuntimeModes.InProcess)
{
public PluginRuntimeMode RuntimeMode =>
PluginRuntimeModes.TryParse(Mode, out var mode) ? mode : PluginRuntimeMode.InProcess;
internal PluginRuntimeConfiguration NormalizeAndValidate(string manifestPath)
{
return this with
{
Mode = PluginRuntimeModes.NormalizeManifestValue(Mode, manifestPath)
};
}
}

View File

@@ -0,0 +1,8 @@
namespace LanMountainDesktop.PluginSdk;
public enum PluginRuntimeMode
{
InProcess = 0,
IsolatedBackground = 1,
IsolatedWindow = 2
}

View File

@@ -0,0 +1,58 @@
namespace LanMountainDesktop.PluginSdk;
public static class PluginRuntimeModes
{
public const string InProcess = "in-proc";
public const string IsolatedBackground = "isolated-background";
public const string IsolatedWindow = "isolated-window";
public static bool TryParse(string? value, out PluginRuntimeMode mode)
{
switch (value?.Trim().ToLowerInvariant())
{
case null:
case "":
case InProcess:
mode = PluginRuntimeMode.InProcess;
return true;
case IsolatedBackground:
mode = PluginRuntimeMode.IsolatedBackground;
return true;
case IsolatedWindow:
mode = PluginRuntimeMode.IsolatedWindow;
return true;
default:
mode = default;
return false;
}
}
public static PluginRuntimeMode Parse(string? value, string sourceName, string propertyName = "runtime.mode")
{
if (TryParse(value, out var mode))
{
return mode;
}
var candidate = string.IsNullOrWhiteSpace(value) ? "<empty>" : value.Trim();
throw new InvalidOperationException(
$"Plugin manifest '{sourceName}' declares unsupported runtime mode '{candidate}' in '{propertyName}'. " +
$"Supported values: '{InProcess}', '{IsolatedBackground}', '{IsolatedWindow}'.");
}
public static string NormalizeManifestValue(string? value, string sourceName, string propertyName = "runtime.mode")
{
return ToManifestValue(Parse(value, sourceName, propertyName));
}
public static string ToManifestValue(PluginRuntimeMode mode)
{
return mode switch
{
PluginRuntimeMode.InProcess => InProcess,
PluginRuntimeMode.IsolatedBackground => IsolatedBackground,
PluginRuntimeMode.IsolatedWindow => IsolatedWindow,
_ => throw new ArgumentOutOfRangeException(nameof(mode), mode, "Unsupported plugin runtime mode.")
};
}
}

View File

@@ -0,0 +1,20 @@
using Microsoft.Extensions.DependencyInjection;
namespace LanMountainDesktop.PluginSdk;
public abstract class PluginWorkerBase : IPluginWorker
{
public virtual void ConfigureServices(IPluginWorkerContext context, IServiceCollection services)
{
}
public virtual Task StartAsync(IPluginWorkerContext context, IServiceProvider services, CancellationToken cancellationToken = default)
{
return Task.CompletedTask;
}
public virtual Task StopAsync(CancellationToken cancellationToken = default)
{
return Task.CompletedTask;
}
}

View File

@@ -0,0 +1,6 @@
namespace LanMountainDesktop.PluginSdk;
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
public sealed class PluginWorkerEntranceAttribute : Attribute
{
}

View File

@@ -5,7 +5,9 @@ Official SDK package for LanMountainDesktop plugins.
## Includes
- `IPlugin`/`PluginBase` entry abstractions
- `IPluginWorker`/`PluginWorkerBase` worker-side entry abstractions for isolated background mode
- `PluginManifest` and shared contract declarations
- `runtime.mode` manifest support for `in-proc`, `isolated-background`, and `isolated-window`
- desktop component registration extensions
- plugin runtime context and host service abstractions
- build-transitive packaging targets for `.laapp` output