docs: 为 CLI、网页编排器和插件协议补充学习向注释

在核心类型、图执行器、stdio 插件和前端入口写清职责与对照路径,不改运行行为。
This commit is contained in:
2026-08-28 11:51:48 +08:00
parent a3cca78592
commit 950d09ddb0
38 changed files with 206 additions and 3 deletions
@@ -2,6 +2,10 @@ using System.Text.Json.Serialization;
namespace MAF1.PluginContract;
/// <summary>
/// 每个插件目录里的 plugin.json。网页宿主扫描这个文件来画节点端口,不会加载插件 DLL。
/// Id 会出现在画布节点的 Type 上。
/// </summary>
public sealed class PluginManifest
{
public string Id { get; set; } = "";
@@ -16,12 +20,14 @@ public sealed class PluginManifest
public List<PluginPort> Outputs { get; set; } = [];
}
/// <summary>如何启动插件进程。Command 可以是相对插件目录的 exe,或 dotnet + dll。</summary>
public sealed class PluginLaunch
{
public string Command { get; set; } = "";
public List<string> Args { get; set; } = [];
}
/// <summary>画布上的一个输入或输出端口。Name 必须和代码里读写的字段一致。</summary>
public sealed class PluginPort
{
public string Name { get; set; } = "";
@@ -30,6 +36,7 @@ public sealed class PluginPort
public bool Required { get; set; }
}
/// <summary>插件声明「我需要哪种凭据」。宿主按 Type 注入,Key 不进流程图 JSON。</summary>
public sealed class PluginCredentialNeed
{
public string Name { get; set; } = "";
@@ -38,6 +45,7 @@ public sealed class PluginCredentialNeed
public string Description { get; set; } = "";
}
/// <summary>宿主写入插件 stdin 的整包请求:业务输入 + 凭据。</summary>
public sealed class PluginRequest
{
public Dictionary<string, object?> Inputs { get; set; } = new(StringComparer.OrdinalIgnoreCase);
@@ -46,6 +54,7 @@ public sealed class PluginRequest
public Dictionary<string, PluginCredentialPayload> Credentials { get; set; } = new(StringComparer.OrdinalIgnoreCase);
}
/// <summary>实际的 endpoint / apiKey / model。只在宿主→插件进程之间传递。</summary>
public sealed class PluginCredentialPayload
{
public string Id { get; set; } = "";
@@ -56,6 +65,7 @@ public sealed class PluginCredentialPayload
public Dictionary<string, string> Extra { get; set; } = new(StringComparer.OrdinalIgnoreCase);
}
/// <summary>插件 JSON 统一 camelCase,和网页前端字段名对齐。</summary>
public static class PluginJson
{
public static readonly System.Text.Json.JsonSerializerOptions Options = new()
+10
View File
@@ -4,8 +4,13 @@ using Microsoft.Extensions.Configuration;
namespace MAF1.PluginContract;
/// <summary>
/// 进程外插件的 stdio 协议:宿主把 PluginRequest JSON 写入 stdin,插件把输出 JSON 写到 stdout。
/// 学习时对照 PluginProcessRunner:那边启动进程、写 stdin、读 stdout。
/// </summary>
public static class PluginStdio
{
/// <summary>插件入口第一步:读完 stdin 再干活。宿主写完会关闭 stdin。</summary>
public static async Task<PluginRequest> ReadRequestAsync(CancellationToken cancellationToken = default)
{
using Stream stdin = Console.OpenStandardInput();
@@ -20,6 +25,7 @@ public static class PluginStdio
return request ?? new PluginRequest();
}
/// <summary>把 Outputs 写成一行 JSON。不要往 stdout 打日志,日志请走 stderr。</summary>
public static async Task WriteOutputsAsync(Dictionary<string, object?> outputs, CancellationToken cancellationToken = default)
{
string json = JsonSerializer.Serialize(outputs, PluginJson.Options);
@@ -27,6 +33,7 @@ public static class PluginStdio
await Console.Out.FlushAsync(cancellationToken);
}
/// <summary>把宿主注入的 LLM 凭据写进环境变量,这样 AgentFactory.Load 能读到 Key。</summary>
public static void ApplyCredentialsToEnvironment(IReadOnlyDictionary<string, PluginCredentialPayload> credentials)
{
foreach (KeyValuePair<string, PluginCredentialPayload> pair in credentials)
@@ -49,6 +56,9 @@ public static class PluginStdio
}
}
/// <summary>
/// 插件自己读天气等配置时用。优先 MAF1_CONTENT_ROOT(宿主 exe 目录),否则向上找 appsettings.json。
/// </summary>
public static IConfiguration LoadHostConfiguration()
{
string contentRoot = Environment.GetEnvironmentVariable("MAF1_CONTENT_ROOT");