196 lines
5.2 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|