feat: 拆分 CLI 与网页宿主,并在无有效城市时暂停等人确认
将原单体 MAF1 拆成 MAF1(控制台工作流)和 MAF1.Web(可视化编排)。 抽城市失败时暂停:网页弹确认、CLI 控制台询问,超时采用默认结束查询; 也可改填城市后继续。共享决策模型放在 MAF1.Core。
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
using MAF1.PluginContract;
|
||||
using MAF1.Web;
|
||||
|
||||
namespace MAF1.Plugins;
|
||||
|
||||
public sealed class NodeCatalogSnapshot
|
||||
{
|
||||
public string PluginsRoot { get; init; } = "";
|
||||
public List<AgentTypeInfo> System { get; init; } = [];
|
||||
public List<AgentTypeInfo> Plugins { get; init; } = [];
|
||||
public List<AgentTypeInfo> Nodes { get; init; } = [];
|
||||
public List<CatalogIssue> Issues { get; init; } = [];
|
||||
public IReadOnlyList<LoadedPlugin> LoadedPlugins { get; init; } = [];
|
||||
}
|
||||
|
||||
public sealed class NodeCatalogService(PluginScanner scanner)
|
||||
{
|
||||
public NodeCatalogSnapshot Load()
|
||||
{
|
||||
PluginScanResult scan = scanner.Scan();
|
||||
List<CatalogIssue> issues = [.. scan.Issues];
|
||||
List<AgentTypeInfo> system = AgentCatalog.SystemNodes
|
||||
.Select(Clone)
|
||||
.ToList();
|
||||
foreach (AgentTypeInfo item in system)
|
||||
{
|
||||
item.Origin = "system";
|
||||
}
|
||||
|
||||
Dictionary<string, AgentTypeInfo> systemByType = system.ToDictionary(n => n.Type, StringComparer.OrdinalIgnoreCase);
|
||||
List<AgentTypeInfo> plugins = [];
|
||||
foreach (LoadedPlugin plugin in scan.Plugins)
|
||||
{
|
||||
AgentTypeInfo info = ToTypeInfo(plugin);
|
||||
if (systemByType.ContainsKey(info.Type))
|
||||
{
|
||||
issues.Add(new CatalogIssue
|
||||
{
|
||||
Level = "info",
|
||||
Source = info.Type,
|
||||
Message = $"插件 `{info.Type}` 覆盖了同名系统节点,画布运行将走插件进程。",
|
||||
});
|
||||
systemByType[info.Type].Overridden = true;
|
||||
}
|
||||
|
||||
plugins.Add(info);
|
||||
}
|
||||
|
||||
Dictionary<string, AgentTypeInfo> merged = systemByType
|
||||
.ToDictionary(kv => kv.Key, kv => kv.Value, StringComparer.OrdinalIgnoreCase);
|
||||
foreach (AgentTypeInfo plugin in plugins)
|
||||
{
|
||||
merged[plugin.Type] = plugin;
|
||||
}
|
||||
|
||||
return new NodeCatalogSnapshot
|
||||
{
|
||||
PluginsRoot = scan.Root,
|
||||
System = system,
|
||||
Plugins = plugins,
|
||||
Nodes = merged.Values.OrderBy(n => n.Origin).ThenBy(n => n.Name).ToList(),
|
||||
Issues = issues,
|
||||
LoadedPlugins = scan.Plugins,
|
||||
};
|
||||
}
|
||||
|
||||
public LoadedPlugin? FindPlugin(string type)
|
||||
=> Load().LoadedPlugins.FirstOrDefault(p => p.Manifest.Id.Equals(type, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
private static AgentTypeInfo ToTypeInfo(LoadedPlugin plugin)
|
||||
{
|
||||
PluginManifest manifest = plugin.Manifest;
|
||||
return new AgentTypeInfo
|
||||
{
|
||||
Type = manifest.Id,
|
||||
Name = string.IsNullOrWhiteSpace(manifest.Name) ? manifest.Id : manifest.Name,
|
||||
Description = manifest.Description,
|
||||
Origin = "plugin",
|
||||
Version = manifest.Version,
|
||||
Folder = plugin.FolderName,
|
||||
Inputs = manifest.Inputs.Select(ToPort).ToList(),
|
||||
Outputs = manifest.Outputs.Select(ToPort).ToList(),
|
||||
Credentials = manifest.Credentials,
|
||||
};
|
||||
}
|
||||
|
||||
private static PortInfo ToPort(PluginPort port)
|
||||
=> new()
|
||||
{
|
||||
Name = port.Name,
|
||||
Type = port.Type,
|
||||
Description = port.Description,
|
||||
Required = port.Required,
|
||||
};
|
||||
|
||||
private static AgentTypeInfo Clone(AgentTypeInfo source)
|
||||
=> new()
|
||||
{
|
||||
Type = source.Type,
|
||||
Name = source.Name,
|
||||
Description = source.Description,
|
||||
Origin = source.Origin,
|
||||
Version = source.Version,
|
||||
Folder = source.Folder,
|
||||
Overridden = source.Overridden,
|
||||
Handler = source.Handler,
|
||||
Inputs = source.Inputs,
|
||||
Outputs = source.Outputs,
|
||||
Credentials = source.Credentials,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user