Files

196 lines
5.2 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Reflection;
using AxCopilot.Handlers;
using AxCopilot.Models;
using AxCopilot.SDK;
using AxCopilot.Services;
namespace AxCopilot.Core;
public class PluginHost
{
private readonly SettingsService _settings;
private readonly CommandResolver _resolver;
private readonly List<IActionHandler> _loadedPlugins = new List<IActionHandler>();
public IReadOnlyList<IActionHandler> LoadedPlugins => _loadedPlugins;
public PluginHost(SettingsService settings, CommandResolver resolver)
{
_settings = settings;
_resolver = resolver;
}
public void LoadAll()
{
_loadedPlugins.Clear();
foreach (PluginEntry item in _settings.Settings.Plugins.Where((PluginEntry p) => p.Enabled))
{
LoadPlugin(item.Path);
}
string path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "AxCopilot", "skills");
if (!Directory.Exists(path))
{
return;
}
foreach (string item2 in Directory.EnumerateFiles(path, "*.skill.json"))
{
LoadJsonSkill(item2);
}
}
private void LoadPlugin(string dllPath)
{
if (!File.Exists(dllPath))
{
LogService.Warn("플러그인 파일 없음: " + dllPath);
return;
}
try
{
Assembly assembly = Assembly.LoadFrom(dllPath);
IEnumerable<Type> enumerable = from t in assembly.GetExportedTypes()
where typeof(IActionHandler).IsAssignableFrom(t) && !t.IsAbstract
select t;
foreach (Type item in enumerable)
{
if (Activator.CreateInstance(item) is IActionHandler actionHandler)
{
_resolver.RegisterHandler(actionHandler);
_loadedPlugins.Add(actionHandler);
LogService.Info("플러그인 로드: " + actionHandler.Metadata.Name + " v" + actionHandler.Metadata.Version);
}
}
}
catch (Exception ex)
{
LogService.Error("플러그인 로드 실패 (" + dllPath + "): " + ex.Message);
}
}
private void LoadJsonSkill(string skillPath)
{
try
{
IActionHandler actionHandler = JsonSkillLoader.Load(skillPath);
if (actionHandler != null)
{
_resolver.RegisterHandler(actionHandler);
_loadedPlugins.Add(actionHandler);
LogService.Info("JSON 스킬 로드: " + actionHandler.Metadata.Name);
}
}
catch (Exception ex)
{
LogService.Error("JSON 스킬 로드 실패 (" + skillPath + "): " + ex.Message);
}
}
public void Reload()
{
LogService.Info("플러그인 전체 재로드 시작");
LoadAll();
}
public int InstallFromZip(string zipPath)
{
if (!File.Exists(zipPath))
{
LogService.Warn("플러그인 zip 파일 없음: " + zipPath);
return 0;
}
string text = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "AxCopilot", "plugins");
Directory.CreateDirectory(text);
int num = 0;
try
{
using ZipArchive zipArchive = ZipFile.OpenRead(zipPath);
string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(zipPath);
string text2 = Path.Combine(text, fileNameWithoutExtension);
Directory.CreateDirectory(text2);
foreach (ZipArchiveEntry entry in zipArchive.Entries)
{
if (!string.IsNullOrEmpty(entry.Name))
{
string text3 = Path.Combine(text2, entry.Name);
if (!Path.GetFullPath(text3).StartsWith(Path.GetFullPath(text2)))
{
LogService.Warn("플러그인 zip 경로 위험: " + entry.FullName);
}
else
{
entry.ExtractToFile(text3, overwrite: true);
}
}
}
foreach (string dllFile in Directory.EnumerateFiles(text2, "*.dll"))
{
if (!_settings.Settings.Plugins.Any((PluginEntry p) => p.Path == dllFile))
{
_settings.Settings.Plugins.Add(new PluginEntry
{
Enabled = true,
Path = dllFile
});
LoadPlugin(dllFile);
num++;
}
}
string text4 = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "AxCopilot", "skills");
Directory.CreateDirectory(text4);
foreach (string item in Directory.EnumerateFiles(text2, "*.skill.json"))
{
string text5 = Path.Combine(text4, Path.GetFileName(item));
File.Copy(item, text5, overwrite: true);
LoadJsonSkill(text5);
num++;
}
if (num > 0)
{
_settings.Save();
}
LogService.Info($"플러그인 설치 완료: {zipPath} → {num}개 핸들러");
}
catch (Exception ex)
{
LogService.Error("플러그인 zip 설치 실패: " + ex.Message);
}
return num;
}
public bool UninstallPlugin(string dllPath)
{
try
{
PluginEntry pluginEntry = _settings.Settings.Plugins.FirstOrDefault((PluginEntry p) => p.Path == dllPath);
if (pluginEntry != null)
{
_settings.Settings.Plugins.Remove(pluginEntry);
_settings.Save();
}
string directoryName = Path.GetDirectoryName(dllPath);
if (directoryName != null && Directory.Exists(directoryName))
{
string path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "AxCopilot", "plugins");
if (Path.GetFullPath(directoryName).StartsWith(Path.GetFullPath(path)))
{
Directory.Delete(directoryName, recursive: true);
}
}
LogService.Info("플러그인 제거 완료: " + dllPath);
return true;
}
catch (Exception ex)
{
LogService.Error("플러그인 제거 실패: " + ex.Message);
return false;
}
}
}