diff --git a/MSLX.Daemon/Controllers/AiAssistantController.cs b/MSLX.Daemon/Controllers/AiAssistantController.cs new file mode 100644 index 00000000..67783fae --- /dev/null +++ b/MSLX.Daemon/Controllers/AiAssistantController.cs @@ -0,0 +1,136 @@ +using System.Text.Json.Nodes; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using MSLX.Daemon.Services; +using MSLX.SDK.Models; + +namespace MSLX.Daemon.Controllers; + +[ApiController] +[Route("api/ai")] +public class AiAssistantController : ControllerBase +{ + private readonly AiService _aiService; + + public AiAssistantController(AiService aiService) + { + _aiService = aiService; + } + + [HttpPost("chat")] + [Authorize(Roles = "admin")] + public async Task Chat([FromBody] ChatRequest request) + { + Response.Headers.Append("Content-Type", "text/event-stream"); + Response.Headers.Append("Cache-Control", "no-cache"); + Response.Headers.Append("Connection", "keep-alive"); + + if (request.Messages == null || request.Messages.Count == 0) + { + await SendSseEventAsync("message", "请输入有效的对话内容。"); + await SendSseEventAsync("done", "[DONE]"); + return; + } + + try + { + var jsonMessages = new JsonArray(); + foreach (var m in request.Messages) + { + jsonMessages.Add(new JsonObject + { + ["role"] = m.Role, + ["content"] = m.Content + }); + } + + await _aiService.ProcessChatAsync( + jsonMessages, + async (chunkText) => + { + await SendSseEventAsync("message", chunkText); + }, + async (toolName, toolData) => + { + var toolPayload = System.Text.Json.JsonSerializer.Serialize(new + { + tool = toolName, + data = toolData + }); + await SendSseEventAsync("tool_executed", toolPayload); + } + ); + } + catch (Exception ex) + { + await SendSseEventAsync("error", ex.Message); + } + finally + { + await SendSseEventAsync("done", "[DONE]"); + } + } + + private async Task SendSseEventAsync(string eventType, string data) + { + try + { + var formattedData = data.Replace("\r", "").Replace("\n", "\\n"); + await Response.WriteAsync($"event: {eventType}\ndata: {formattedData}\n\n"); + await Response.Body.FlushAsync(); + } + catch (Exception ex) + { + // 客户端已断开(如用户中断对话),忽略写入异常 + Console.WriteLine($"SSE 写入失败 (客户端可能已断开): {ex.Message}"); + } + } + + [HttpPost("confirm-tool")] + [Authorize(Roles = "admin")] + public async Task ConfirmTool([FromBody] ConfirmToolRequest request) + { + string? toolName = null; + object? toolData = null; + + var (success, message, _) = await _aiService.ConfirmPendingToolAsync( + request.ConfirmationId, + request.Approved, + async (name, data) => + { + toolName = name; + toolData = data; + await Task.CompletedTask; + }); + + return Ok(new ApiResponse + { + Code = success ? 200 : 400, + Message = message, + Data = new + { + success, + message, + tool = toolName, + data = toolData + } + }); + } +} + +public class ConfirmToolRequest +{ + public string ConfirmationId { get; set; } = ""; + public bool Approved { get; set; } +} + +public class ChatRequest +{ + public List? Messages { get; set; } +} + +public class ChatMessageDto +{ + public string Role { get; set; } = "user"; + public string Content { get; set; } = ""; +} diff --git a/MSLX.Daemon/Controllers/AiSettingsController.cs b/MSLX.Daemon/Controllers/AiSettingsController.cs new file mode 100644 index 00000000..56e843f8 --- /dev/null +++ b/MSLX.Daemon/Controllers/AiSettingsController.cs @@ -0,0 +1,54 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using MSLX.Daemon.Utils.ConfigUtils; +using MSLX.SDK.Models; + +namespace MSLX.Daemon.Controllers; + +[ApiController] +[Route("api/settings/ai")] +public class AiSettingsController : ControllerBase +{ + [HttpGet] + [Authorize(Roles = "admin")] + public IActionResult GetAiSettings() + { + var config = IConfigBase.Config.ReadConfig(); + return Ok(new ApiResponse + { + Code = 200, + Message = "获取成功", + Data = new + { + AiEnabled = (bool?)(config["aiEnabled"]) ?? false, + AiApiKey = (string?)(config["aiApiKey"]) ?? "", + AiBaseUrl = (string?)(config["aiBaseUrl"]) ?? "https://api.deepseek.com/v1", + AiModelName = (string?)(config["aiModelName"]) ?? "deepseek-chat" + } + }); + } + + [HttpPost] + [Authorize(Roles = "admin")] + public IActionResult UpdateAiSettings([FromBody] AiSettingsRequest request) + { + IConfigBase.Config.WriteConfigKey("aiEnabled", request.AiEnabled); + IConfigBase.Config.WriteConfigKey("aiApiKey", request.AiApiKey ?? ""); + IConfigBase.Config.WriteConfigKey("aiBaseUrl", request.AiBaseUrl ?? "https://api.deepseek.com/v1"); + IConfigBase.Config.WriteConfigKey("aiModelName", request.AiModelName ?? "deepseek-chat"); + + return Ok(new ApiResponse + { + Code = 200, + Message = "AI 配置更新成功" + }); + } +} + +public class AiSettingsRequest +{ + public bool AiEnabled { get; set; } + public string? AiApiKey { get; set; } + public string? AiBaseUrl { get; set; } + public string? AiModelName { get; set; } +} diff --git a/MSLX.Daemon/Hubs/AiChatHub.cs b/MSLX.Daemon/Hubs/AiChatHub.cs new file mode 100644 index 00000000..9ac63057 --- /dev/null +++ b/MSLX.Daemon/Hubs/AiChatHub.cs @@ -0,0 +1,74 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.SignalR; +using MSLX.Daemon.Services; +using System.Text.Json.Nodes; + +namespace MSLX.Daemon.Hubs; + +[Authorize(Roles = "admin")] +public class AiChatHub : Hub +{ + private readonly AiService _aiService; + private readonly ILogger _logger; + + public AiChatHub(AiService aiService, ILogger logger) + { + _aiService = aiService; + _logger = logger; + } + + public async Task SendMessage(JsonArray messages) + { + _logger.LogInformation("收到 SignalR AI 对话请求,ConnectionId: {ConnectionId}", Context.ConnectionId); + + try + { + await _aiService.ProcessChatAsync( + messages, + async (chunk) => + { + await Clients.Caller.SendAsync("ChatChunk", chunk); + }, + async (toolName, toolData) => + { + await Clients.Caller.SendAsync("ToolExecuted", toolName, toolData); + } + ); + + await Clients.Caller.SendAsync("ChatComplete"); + } + catch (Exception ex) + { + _logger.LogError(ex, "SignalR AI 对话处理异常"); + try + { + await Clients.Caller.SendAsync("ChatError", ex.Message); + } + catch (Exception sendEx) + { + _logger.LogWarning(sendEx, "向已断开的 SignalR 客户端发送 ChatError 失败"); + } + } + } + + public async Task ConfirmToolAction(string confirmationId, bool approved) + { + try + { + var (success, message, data) = await _aiService.ConfirmPendingToolAsync( + confirmationId, + approved, + async (toolName, toolData) => + { + await Clients.Caller.SendAsync("ToolExecuted", toolName, toolData); + }); + + return new { success, message, data }; + } + catch (Exception ex) + { + _logger.LogError(ex, "SignalR AI 敏感操作确认异常"); + return new { success = false, message = ex.Message, data = (object?)null }; + } + } +} diff --git a/MSLX.Daemon/MSLX.Daemon.csproj b/MSLX.Daemon/MSLX.Daemon.csproj index 0301de65..6ee6e30b 100644 --- a/MSLX.Daemon/MSLX.Daemon.csproj +++ b/MSLX.Daemon/MSLX.Daemon.csproj @@ -60,4 +60,8 @@ + + + + diff --git a/MSLX.Daemon/Program.cs b/MSLX.Daemon/Program.cs index b346edc7..d29b5d9c 100644 --- a/MSLX.Daemon/Program.cs +++ b/MSLX.Daemon/Program.cs @@ -1,4 +1,4 @@ -using Microsoft.AspNetCore.HttpOverrides; +using Microsoft.AspNetCore.HttpOverrides; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.StaticFiles; using Microsoft.Extensions.FileProviders; @@ -190,6 +190,7 @@ builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); +builder.Services.AddSingleton(); // 插件的一些服务 var pluginManager = new PluginManager(); builder.Services.AddSingleton(pluginManager); @@ -435,6 +436,7 @@ app.MapHub("/api/hubs/instanceControlHub"); app.MapHub("/api/hubs/system"); app.MapHub("/api/hubs/daemonUpdate"); +app.MapHub("/api/hubs/aiChatHub"); app.MapControllers(); diff --git a/MSLX.Daemon/Services/AiService.cs b/MSLX.Daemon/Services/AiService.cs new file mode 100644 index 00000000..fb2f4ab0 --- /dev/null +++ b/MSLX.Daemon/Services/AiService.cs @@ -0,0 +1,1864 @@ +using System.Collections.Concurrent; +using System.Text; +using System.Text.Json; +using System.Text.Json.Nodes; +using System.Text.RegularExpressions; +using Microsoft.Extensions.Caching.Memory; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json.Linq; +using MSLX.Daemon.Utils; +using MSLX.Daemon.Utils.BackgroundTasks; +using MSLX.Daemon.Utils.ConfigUtils; +using MSLX.SDK.IServices; +using MSLX.SDK.Models; +using MSLX.SDK.Models.Instance; +using MSLX.SDK.Models.Tasks; + +namespace MSLX.Daemon.Services; + +public class AiService +{ + private readonly HttpClient _httpClient; + private readonly IMCServerService _mcServerService; + private readonly IBackgroundTaskQueue _createTaskQueue; + private readonly IServiceScopeFactory _scopeFactory; + private readonly IMemoryCache _memoryCache; + private readonly ILogger _logger; + private readonly ConcurrentDictionary _pendingToolOperations = new(); + private static readonly TimeSpan PendingOperationTtl = TimeSpan.FromMinutes(30); + + public AiService( + IMCServerService mcServerService, + IBackgroundTaskQueue createTaskQueue, + IServiceScopeFactory scopeFactory, + IMemoryCache memoryCache, + ILogger logger) + { + _httpClient = new HttpClient(); + _mcServerService = mcServerService; + _createTaskQueue = createTaskQueue; + _scopeFactory = scopeFactory; + _memoryCache = memoryCache; + _logger = logger; + } + + public async Task ProcessChatAsync( + JsonArray messages, + Func onChunkReceived, + Func onToolExecuted) + { + var config = IConfigBase.Config.ReadConfig(); + bool enabled = (bool?)(config["aiEnabled"]) ?? false; + string apiKey = (string?)(config["aiApiKey"]) ?? ""; + string baseUrl = (string?)(config["aiBaseUrl"]) ?? "https://api.deepseek.com/v1"; + string modelName = (string?)(config["aiModelName"]) ?? "deepseek-chat"; + var systemPrompt = AiSkillManager.GetSystemPrompt(); + + if (!enabled || string.IsNullOrWhiteSpace(apiKey)) + { + await onChunkReceived("⚠️ AI 助手尚未启用或未设置 API Key,请先前往系统的【AI 助手设置】中配置。"); + return; + } + + var requestUrl = $"{baseUrl.TrimEnd('/')}/chat/completions"; + + var reqMessages = new JsonArray + { + new JsonObject + { + ["role"] = "system", + ["content"] = systemPrompt + } + }; + + foreach (var msg in messages) + { + if (msg != null) + { + reqMessages.Add(msg.DeepClone()); + } + } + + var tools = GetToolsDefinition(); + + int maxTurns = 15; + while (maxTurns-- > 0) + { + var payload = new JsonObject + { + ["model"] = modelName, + ["messages"] = reqMessages.DeepClone(), + ["tools"] = tools.DeepClone(), + ["stream"] = true, + ["stream_options"] = new JsonObject + { + ["include_usage"] = true + } + }; + + using var request = new HttpRequestMessage(HttpMethod.Post, requestUrl); + request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", apiKey); + request.Content = new StringContent(payload.ToJsonString(), Encoding.UTF8, "application/json"); + + try + { + using var response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead); + + if (!response.IsSuccessStatusCode) + { + var responseContent = await response.Content.ReadAsStringAsync(); + _logger.LogError("调用大模型失败 HTTP {Status}: {Body}", response.StatusCode, responseContent); + await onChunkReceived($"调用大模型失败 (HTTP {(int)response.StatusCode}): {responseContent}"); + return; + } + + using var responseStream = await response.Content.ReadAsStreamAsync(); + using var reader = new StreamReader(responseStream, Encoding.UTF8); + + var assistantMessage = new JsonObject { ["role"] = "assistant" }; + var toolCallsByIndex = new Dictionary(); + var toolCallNameBuilders = new Dictionary(); + var toolCallArgsBuilders = new Dictionary(); + var textContent = new StringBuilder(); + + string? line; + while ((line = await reader.ReadLineAsync()) != null) + { + if (string.IsNullOrWhiteSpace(line) || !line.StartsWith("data:")) continue; + + var data = line.Substring(5).Trim(); + if (data == "[DONE]") break; + + JsonObject? chunk; + try + { + chunk = JsonNode.Parse(data)?.AsObject(); + } + catch + { + continue; + } + + var choices = chunk?["choices"]?.AsArray(); + if (choices == null || choices.Count == 0) continue; + + var delta = choices[0]?["delta"]?.AsObject(); + if (delta == null) + { + // 兼容个别不真正流式返回、直接给整段 message 的接口 + delta = choices[0]?["message"]?.AsObject(); + } + if (delta == null) continue; + + var contentDelta = GetStringOrNull(delta["content"]); + if (!string.IsNullOrEmpty(contentDelta)) + { + textContent.Append(contentDelta); + await onChunkReceived(contentDelta); + } + + var deltaToolCalls = delta["tool_calls"]?.AsArray(); + if (deltaToolCalls == null) continue; + + foreach (var deltaToolCall in deltaToolCalls) + { + if (deltaToolCall is not JsonObject toolCallObj) continue; + + int index = toolCallObj["index"] is JsonValue indexValue && indexValue.TryGetValue(out int idx) + ? idx + : toolCallsByIndex.Count; + + if (!toolCallsByIndex.TryGetValue(index, out var toolCallBuilder)) + { + toolCallBuilder = new JsonObject + { + ["id"] = GetStringOrNull(toolCallObj["id"]) ?? $"call_{index}", + ["type"] = GetStringOrNull(toolCallObj["type"]) ?? "function", + ["function"] = new JsonObject() + }; + toolCallsByIndex[index] = toolCallBuilder; + toolCallNameBuilders[index] = new StringBuilder(); + toolCallArgsBuilders[index] = new StringBuilder(); + } + + var idDelta = GetStringOrNull(toolCallObj["id"]); + if (!string.IsNullOrEmpty(idDelta) && string.IsNullOrEmpty(GetStringOrNull(toolCallBuilder["id"]))) + { + toolCallBuilder["id"] = idDelta; + } + + var functionObj = toolCallObj["function"]?.AsObject(); + if (functionObj == null) continue; + + var nameDelta = GetStringOrNull(functionObj["name"]); + if (!string.IsNullOrEmpty(nameDelta)) + { + toolCallNameBuilders[index].Append(nameDelta); + } + + var argsDelta = GetStringOrNull(functionObj["arguments"]); + if (!string.IsNullOrEmpty(argsDelta)) + { + toolCallArgsBuilders[index].Append(argsDelta); + } + } + } + + if (toolCallsByIndex.Count > 0) + { + if (textContent.Length > 0) + { + assistantMessage["content"] = textContent.ToString(); + } + + var toolCallsArray = new JsonArray(); + foreach (var kvp in toolCallsByIndex.OrderBy(k => k.Key)) + { + var functionObj = kvp.Value["function"]!.AsObject(); + functionObj["name"] = toolCallNameBuilders[kvp.Key].ToString(); + functionObj["arguments"] = toolCallArgsBuilders[kvp.Key].ToString(); + toolCallsArray.Add(kvp.Value); + } + + assistantMessage["tool_calls"] = toolCallsArray; + reqMessages.Add(assistantMessage); + + foreach (var call in toolCallsArray) + { + var toolCallId = call?["id"]?.ToString() ?? ""; + var functionObj = call?["function"]?.AsObject(); + var functionName = functionObj?["name"]?.ToString() ?? ""; + var argumentsStr = functionObj?["arguments"]?.ToString() ?? "{}"; + + _logger.LogInformation("AI 触发 Tool Call: {Name}, Args: {Args}", functionName, argumentsStr); + + var toolResult = await ExecuteToolAsync(functionName, argumentsStr, onToolExecuted); + + reqMessages.Add(new JsonObject + { + ["role"] = "tool", + ["tool_call_id"] = toolCallId, + ["content"] = toolResult + }); + } + + continue; + } + + var contentText = textContent.ToString(); + + List? suggestions = null; + var sugMatch = Regex.Match(contentText, @"<<\[.*?\])>>>", RegexOptions.Singleline); + if (sugMatch.Success) + { + try + { + suggestions = JsonSerializer.Deserialize>(sugMatch.Groups["json"].Value); + contentText = Regex.Replace(contentText, @"<<>>", "", RegexOptions.Singleline).Trim(); + } + catch (Exception ex) + { + _logger.LogWarning(ex, "解析建议回复 JSON 失败"); + } + } + + if (suggestions != null && suggestions.Count > 0) + { + await onToolExecuted("suggested_replies", suggestions); + } + + // 内容已在流式过程中逐块推送,这里只负责处理文本内嵌工具调用(如 DSML),不再重复发送整段文本 + await TryHandleDsmlTextToolCallsAsync(contentText, onToolExecuted); + break; + } + catch (Exception ex) + { + _logger.LogError(ex, "处理 AI 对话异常"); + await onChunkReceived($"处理请求时发生异常: {ex.Message}"); + break; + } + } + } + + private string GetJavaMajorVersion(string versionStr) + { + if (string.IsNullOrWhiteSpace(versionStr)) return "17"; + var match = Regex.Match(versionStr, @"(?:1\.)?(?\d+)"); + return match.Success ? match.Groups["ver"].Value : "17"; + } + + private int GetRecommendedJavaMajor(string mcVersion) + { + if (string.IsNullOrWhiteSpace(mcVersion)) return 17; + + var match26 = Regex.Match(mcVersion, @"\b(2\d(?:\.\d+)*)\b"); + if (match26.Success) + { + if (float.TryParse(match26.Value, out float verNum) && verNum >= 26.1f) + return 25; + } + + var match = Regex.Match(mcVersion, @"\b(\d+(?:\.\d+)+)\b"); + string cleanVersion = match.Success ? match.Value : mcVersion.Trim(); + + int CompareVersions(string v1, string v2) + { + var parts1 = v1.Split('.').Select(p => int.TryParse(p, out int n) ? n : 0).ToArray(); + var parts2 = v2.Split('.').Select(p => int.TryParse(p, out int n) ? n : 0).ToArray(); + int maxLen = Math.Max(parts1.Length, parts2.Length); + for (int i = 0; i < maxLen; i++) + { + int p1 = i < parts1.Length ? parts1[i] : 0; + int p2 = i < parts2.Length ? parts2[i] : 0; + if (p1 != p2) return p1 - p2; + } + return 0; + } + + if (CompareVersions(cleanVersion, "26.1") >= 0) return 25; + if (CompareVersions(cleanVersion, "1.20.5") >= 0) return 21; + if (CompareVersions(cleanVersion, "1.18") >= 0) return 17; + if (CompareVersions(cleanVersion, "1.17") >= 0) return 16; + return 8; + } + + private JsonNode? ConvertJTokenToJsonNode(object? data) + { + if (data is JToken token) + { + try + { + return JsonNode.Parse(token.ToString(Newtonsoft.Json.Formatting.None)); + } + catch + { + return null; + } + } + return null; + } + + private async Task TryHandleDsmlTextToolCallsAsync(string contentText, Func onToolExecuted) + { + if (string.IsNullOrWhiteSpace(contentText) || !contentText.Contains("<|DSML|invoke")) + { + return null; + } + + try + { + var invokeMatch = Regex.Match(contentText, @"<|DSML|invoke\s+name=""(?[^""]+)""[^>]*>(?"); + if (invokeMatch.Success) + { + string rawName = invokeMatch.Groups["name"].Value; + string body = invokeMatch.Groups["body"].Value; + + string toolName = rawName switch + { + "create_server" => "create_mc_server", + "create_mc_server" => "create_mc_server", + "query_cores" => "query_available_server_cores", + "query_java" => "query_java_environments", + "check_status" => "query_creation_status", + "update_instance" => "update_instance_settings", + _ => rawName + }; + + var argsObj = new JsonObject(); + var paramMatches = Regex.Matches(body, @"<|DSML|parameter\s+name=""(?[^""]+)""[^>]*>(?[^<]*)"); + foreach (Match pm in paramMatches) + { + string pname = pm.Groups["pname"].Value; + string pval = pm.Groups["pval"].Value; + + string mappedPName = pname switch + { + "core_name" => "server_type", + "version" => "mc_version", + "name" => "server_name", + _ => pname + }; + + argsObj[mappedPName] = pval; + } + + _logger.LogInformation("解析并拦截文本中的 DSML Tool Call: {Name}, Args: {Args}", toolName, argsObj.ToJsonString()); + string toolResult = await ExecuteToolAsync(toolName, argsObj.ToJsonString(), onToolExecuted); + + string cleanText = Regex.Replace(contentText, @"<|DSML|tool_calls[\s\S]*?", "").Trim(); + return string.IsNullOrWhiteSpace(cleanText) ? toolResult : $"{cleanText}\n\n{toolResult}"; + } + } + catch (Exception ex) + { + _logger.LogError(ex, "解析 DSML 文本标签失败"); + } + + return null; + } + + private JsonArray GetToolsDefinition() + { + return new JsonArray + { + new JsonObject + { + ["type"] = "function", + ["function"] = new JsonObject + { + ["name"] = "update_instance_settings", + ["description"] = "修改 MSLX 服务器基础实例属性(如切换绑定的 Java 环境,调整最大/最小内存 MB,修改运行核心 Core 文件名,附加 JVM 参数等系统级配置)。Java 参数传入 'suitable' 或 'auto' 时系统会自动根据规则精准推导(NeoForge 26.x / MC 26.1+ 强制绑定 Java 25;1.20.5~1.21 绑定 Java 21;1.18~1.20.4 绑定 Java 17;1.17 绑定 Java 16;1.13及更低绑定 Java 8)。", + ["parameters"] = new JsonObject + { + ["type"] = "object", + ["properties"] = new JsonObject + { + ["server_id"] = new JsonObject { ["type"] = "number", ["description"] = "服务器 ID,未指定时结合【当前用户界面上下文】的主机 ID 或最新服务器" }, + ["java"] = new JsonObject { ["type"] = "string", ["description"] = "Java 环境标识,如 'MSLX://Java/25'、'suitable' (自动精准推导)、'MSLX://Java/21'、'MSLX://Java/17'、'MSLX://Java/8'" }, + ["name"] = new JsonObject { ["type"] = "string", ["description"] = "服务器实例名称" }, + ["max_m"] = new JsonObject { ["type"] = "integer", ["description"] = "最大内存 (MB),如 4096" }, + ["min_m"] = new JsonObject { ["type"] = "integer", ["description"] = "最小内存 (MB),如 1024" }, + ["core"] = new JsonObject { ["type"] = "string", ["description"] = "核心 JAR 文件名,如 'neoforge-26.2.jar'" }, + ["args"] = new JsonObject { ["type"] = "string", ["description"] = "附加 JVM 启动参数" }, + ["auto_restart"] = new JsonObject { ["type"] = "boolean", ["description"] = "崩溃时是否自动重启" } + } + } + } + }, + new JsonObject + { + ["type"] = "function", + ["function"] = new JsonObject + { + ["name"] = "list_server_files", + ["description"] = "列出指定服务器实例目录或子目录下的所有文件和文件夹列表。", + ["parameters"] = new JsonObject + { + ["type"] = "object", + ["properties"] = new JsonObject + { + ["server_id"] = new JsonObject { ["type"] = "number", ["description"] = "服务器 ID,未指定时结合【当前用户界面上下文】的主机 ID 或最新服务器" }, + ["dir_path"] = new JsonObject { ["type"] = "string", ["description"] = "相对目录路径,如 '' (根目录), 'plugins', 'mods', 'logs'" } + } + } + } + }, + new JsonObject + { + ["type"] = "function", + ["function"] = new JsonObject + { + ["name"] = "read_server_file", + ["description"] = "读取指定服务器实例目录下的任意文本文件(如 server.properties, eula.txt, spigot.yml, paper-global.yml, 插件/模组配置文件及运行日志等)。底层采用 FileShare.ReadWrite 共享模式,即使服务器正在运行中也可安全无冲突读取进行日志排查。", + ["parameters"] = new JsonObject + { + ["type"] = "object", + ["properties"] = new JsonObject + { + ["server_id"] = new JsonObject { ["type"] = "number", ["description"] = "服务器 ID,未指定时结合【当前用户界面上下文】的主机 ID 或最新服务器" }, + ["file_path"] = new JsonObject { ["type"] = "string", ["description"] = "相对文件路径,如 'server.properties', 'logs/latest.log', 'logs/debug.log', 'plugins/Essentials/config.yml'" } + }, + ["required"] = new JsonArray { "file_path" } + } + } + }, + new JsonObject + { + ["type"] = "function", + ["function"] = new JsonObject + { + ["name"] = "write_server_file", + ["description"] = "创建或覆盖写入指定服务器实例目录下的文本文件(如修改 server.properties, eula.txt, 插件配置)。若目标文件已存在,系统会自动挂起操作并在前端弹出授权确认卡片,用户点击确认后才会真正写入,无需在参数中指定确认状态。", + ["parameters"] = new JsonObject + { + ["type"] = "object", + ["properties"] = new JsonObject + { + ["server_id"] = new JsonObject { ["type"] = "number", ["description"] = "服务器 ID,未指定时结合【当前用户界面上下文】的主机 ID 或最新服务器" }, + ["file_path"] = new JsonObject { ["type"] = "string", ["description"] = "相对文件路径,如 'server.properties', 'eula.txt', 'spigot.yml'" }, + ["content"] = new JsonObject { ["type"] = "string", ["description"] = "写入文件的完整文本内容" } + }, + ["required"] = new JsonArray { "file_path", "content" } + } + } + }, + new JsonObject + { + ["type"] = "function", + ["function"] = new JsonObject + { + ["name"] = "delete_server_file", + ["description"] = "删除指定服务器实例目录下的文件或文件夹(如删除存档 'world'、旧插件、模组或日志文件)。【请直接果断调用本工具,严禁在回复文本中反问用户;系统会自动挂起操作并在前端弹出确认卡片,用户点击确认授权后才会真正删除】。", + ["parameters"] = new JsonObject + { + ["type"] = "object", + ["properties"] = new JsonObject + { + ["server_id"] = new JsonObject { ["type"] = "number", ["description"] = "服务器 ID,未指定时结合【当前用户界面上下文】的主机 ID 或最新服务器" }, + ["file_path"] = new JsonObject { ["type"] = "string", ["description"] = "相对文件或目录路径,如 'world', 'plugins/old_plugin.jar', 'mods/test.jar'" } + }, + ["required"] = new JsonArray { "file_path" } + } + } + }, + new JsonObject + { + ["type"] = "function", + ["function"] = new JsonObject + { + ["name"] = "query_creation_status", + ["description"] = "查询指定服务器实例后台建服与安装的最新进度、状态消息及是否成功/失败。", + ["parameters"] = new JsonObject + { + ["type"] = "object", + ["properties"] = new JsonObject + { + ["server_id"] = new JsonObject { ["type"] = "number", ["description"] = "服务器 ID" } + }, + ["required"] = new JsonArray { "server_id" } + } + } + }, + new JsonObject + { + ["type"] = "function", + ["function"] = new JsonObject + { + ["name"] = "query_java_environments", + ["description"] = "扫描并查询当前主机上已安装的所有 Java 环境列表(如 Java 8, 16, 17, 21, 25 等及对应的 MSLX 路径标识)。", + ["parameters"] = new JsonObject + { + ["type"] = "object", + ["properties"] = new JsonObject() + } + } + }, + new JsonObject + { + ["type"] = "function", + ["function"] = new JsonObject + { + ["name"] = "query_available_server_cores", + ["description"] = "向 MSL 镜像站实时查询当前支持的服务端核心类型(如 neoforge, forge, paper, fabric, purpur, mohist, arclight, vanilla 等)或指定核心所支持的所有 MC 游戏版本与构建号列表。", + ["parameters"] = new JsonObject + { + ["type"] = "object", + ["properties"] = new JsonObject + { + ["core_name"] = new JsonObject { ["type"] = "string", ["description"] = "核心名称(如 'neoforge', 'paper', 'forge', 'fabric')。留空时返回全量支持的核心分类与类型列表。" } + } + } + } + }, + new JsonObject + { + ["type"] = "function", + ["function"] = new JsonObject + { + ["name"] = "create_mc_server", + ["description"] = "在 MSLX 面板中创建并部署 Minecraft 服务器实例。支持 4 种开服模式:1. 快速在线部署模式 (Paper/NeoForge/Fabric/Forge/Vanilla/Spigot/Mohist 等);2. 整合包/压缩包部署模式 (支持本地 .zip 绝对路径或远程 URL 下载);3. MCDReforged (MCDR) 架构模式;4. 自定义/导入模式 (自定义主机路径、核心与 JVM 参数)。系统会根据 MC 版本自动精确推导并绑定最匹配的 Java 版本。", + ["parameters"] = new JsonObject + { + ["type"] = "object", + ["properties"] = new JsonObject + { + ["server_name"] = new JsonObject { ["type"] = "string", ["description"] = "服务器实例名称,如 '我的 Fabric 1.20.2 服务器'" }, + ["server_type"] = new JsonObject { ["type"] = "string", ["description"] = "核心类型,如 paper, fabric, neoforge, forge, spigot, vanilla, mohist, arclight, custom, package, mcdr" }, + ["mc_version"] = new JsonObject { ["type"] = "string", ["description"] = "Minecraft 游戏版本号或核心版本号,如 '1.20.2', '26.2', '1.12.2'" }, + ["java"] = new JsonObject { ["type"] = "string", ["description"] = "可选 Java 环境标识(如 'MSLX://Java/21'),留空时系统按 MC 版本自动推导并绑定" }, + ["max_m"] = new JsonObject { ["type"] = "integer", ["description"] = "最大分配内存 MB,默认 2048" }, + ["min_m"] = new JsonObject { ["type"] = "integer", ["description"] = "最小分配内存 MB,默认 1024" }, + ["args"] = new JsonObject { ["type"] = "string", ["description"] = "附加 JVM 启动参数" }, + ["ignore_eula"] = new JsonObject { ["type"] = "boolean", ["description"] = "是否自动同意 EULA,默认 true" }, + ["server_port"] = new JsonObject { ["type"] = "integer", ["description"] = "服务器映射端口,如 25565" }, + ["path"] = new JsonObject { ["type"] = "string", ["description"] = "自定义开服宿主机绝对路径,留空使用默认存放目录" }, + ["package_local_path"] = new JsonObject { ["type"] = "string", ["description"] = "【整合包模式】宿主机上的整合包 .zip 文件的绝对路径" }, + ["package_url"] = new JsonObject { ["type"] = "string", ["description"] = "【整合包模式】整合包 .zip 的远程下载地址 (http/https)" }, + ["mcdr"] = new JsonObject { ["type"] = "boolean", ["description"] = "【MCDR 模式】是否启用 MCDReforged 架构控制端" }, + ["mcdr_python"] = new JsonObject { ["type"] = "string", ["description"] = "【MCDR 模式】Python 可执行文件路径,默认为 'python'" }, + ["mcdr_handler"] = new JsonObject { ["type"] = "string", ["description"] = "【MCDR 模式】Handler 类型,如 'vanilla', 'paper', 'fabric', 'custom'" }, + ["mcdr_install"] = new JsonObject { ["type"] = "boolean", ["description"] = "【MCDR 模式】是否自动 pip 安装 MCDReforged,默认 true" }, + ["mcdr_pip_mirror"] = new JsonObject { ["type"] = "string", ["description"] = "【MCDR 模式】pip 镜像源 URL" }, + ["core_url"] = new JsonObject { ["type"] = "string", ["description"] = "【自定义核心】核心 JAR 文件的直接下载链接" }, + ["core_filename"] = new JsonObject { ["type"] = "string", ["description"] = "【自定义核心】核心 JAR 文件名,如 'server.jar'" } + } + } + } + }, + new JsonObject + { + ["type"] = "function", + ["function"] = new JsonObject + { + ["name"] = "update_server_config", + ["description"] = "更新服务器的核心属性(如修改 server.properties 中的端口 server_port、正版验证 online_mode、PVP pvp、最大玩家数 max_players、MOTD motd)。", + ["parameters"] = new JsonObject + { + ["type"] = "object", + ["properties"] = new JsonObject + { + ["server_id"] = new JsonObject { ["type"] = "number", ["description"] = "服务器 ID,未指定时结合【当前用户界面上下文】的主机 ID 或最新服务器" }, + ["server_port"] = new JsonObject { ["type"] = "integer", ["description"] = "服务器通信端口,如 18889" }, + ["online_mode"] = new JsonObject { ["type"] = "boolean", ["description"] = "正版验证设置" }, + ["pvp"] = new JsonObject { ["type"] = "boolean", ["description"] = "PVP 设置" }, + ["max_players"] = new JsonObject { ["type"] = "integer", ["description"] = "最大玩家数" }, + ["motd"] = new JsonObject { ["type"] = "string", ["description"] = "服务器标语 (MOTD)" } + } + } + } + }, + new JsonObject + { + ["type"] = "function", + ["function"] = new JsonObject + { + ["name"] = "control_server", + ["description"] = "控制服务器的启动、停止或重启。", + ["parameters"] = new JsonObject + { + ["type"] = "object", + ["properties"] = new JsonObject + { + ["server_id"] = new JsonObject { ["type"] = "number", ["description"] = "服务器 ID,未指定时结合【当前用户界面上下文】的主机 ID 或最新服务器" }, + ["action"] = new JsonObject { ["type"] = "string", ["description"] = "动作,取值为 start, stop, restart" } + }, + ["required"] = new JsonArray { "action" } + } + } + }, + new JsonObject + { + ["type"] = "function", + ["function"] = new JsonObject + { + ["name"] = "query_server_status", + ["description"] = "查询当前所有服务器或指定服务器的运行状态列表。", + ["parameters"] = new JsonObject + { + ["type"] = "object", + ["properties"] = new JsonObject + { + ["server_id"] = new JsonObject { ["type"] = "number", ["description"] = "可选服务器 ID" } + } + } + } + }, + new JsonObject + { + ["type"] = "function", + ["function"] = new JsonObject + { + ["name"] = "read_server_log", + ["description"] = "专用于诊断服务器崩溃与实时日志排查。读取服务器 `logs/latest.log` 末尾指定行数的文本,或直接检索 `crash-reports/` 目录下最新的崩溃报告文件。采用 FileShare.ReadWrite 共享模式,即使服务器运行中或崩溃卡死时也可安全读取。", + ["parameters"] = new JsonObject + { + ["type"] = "object", + ["properties"] = new JsonObject + { + ["server_id"] = new JsonObject { ["type"] = "number", ["description"] = "服务器 ID,未指定时结合【当前用户界面上下文】的主机 ID 或最新服务器" }, + ["log_type"] = new JsonObject { ["type"] = "string", ["description"] = "日志类型,取值为 'latest' (运行日志) 或 'crash_report' (崩溃文件),默认为 'latest'" }, + ["tail_lines"] = new JsonObject { ["type"] = "integer", ["description"] = "读取末尾行数,默认 100 行" } + } + } + } + }, + new JsonObject + { + ["type"] = "function", + ["function"] = new JsonObject + { + ["name"] = "copy_server_file", + ["description"] = "在指定服务器实例目录下复制文件或文件夹(如备份配置文件 `server.properties` 为 `server.properties.bak`,备份存档,或复制 Mod/插件)。", + ["parameters"] = new JsonObject + { + ["type"] = "object", + ["properties"] = new JsonObject + { + ["server_id"] = new JsonObject { ["type"] = "number", ["description"] = "服务器 ID,未指定时结合【当前用户界面上下文】的主机 ID 或最新服务器" }, + ["source_path"] = new JsonObject { ["type"] = "string", ["description"] = "源相对路径,如 'server.properties', 'mods/test.jar', 'world'" }, + ["destination_path"] = new JsonObject { ["type"] = "string", ["description"] = "目标相对路径,如 'server.properties.bak', 'mods_bak/test.jar', 'world_backup'" } + }, + ["required"] = new JsonArray { "source_path", "destination_path" } + } + } + }, + new JsonObject + { + ["type"] = "function", + ["function"] = new JsonObject + { + ["name"] = "move_server_file", + ["description"] = "在指定服务器实例目录下移动或重命名文件/文件夹(如将崩服的 Mod `mod.jar` 重命名为 `mod.jar.disabled` 以禁用模组,移动备份,重命名地图文件夹等)。", + ["parameters"] = new JsonObject + { + ["type"] = "object", + ["properties"] = new JsonObject + { + ["server_id"] = new JsonObject { ["type"] = "number", ["description"] = "服务器 ID,未指定时结合【当前用户界面上下文】的主机 ID 或最新服务器" }, + ["source_path"] = new JsonObject { ["type"] = "string", ["description"] = "源相对路径,如 'mods/badmod.jar', 'server.properties'" }, + ["destination_path"] = new JsonObject { ["type"] = "string", ["description"] = "目标相对路径,如 'mods/badmod.jar.disabled', 'server.properties.old'" } + }, + ["required"] = new JsonArray { "source_path", "destination_path" } + } + } + }, + new JsonObject + { + ["type"] = "function", + ["function"] = new JsonObject + { + ["name"] = "query_system_metrics", + ["description"] = "实时查询宿主机及指定 Minecraft 服务器实例的 CPU 使用率、已用/可用系统内存、磁盘空间、网卡流量以及运行实例性能状态。", + ["parameters"] = new JsonObject + { + ["type"] = "object", + ["properties"] = new JsonObject + { + ["server_id"] = new JsonObject { ["type"] = "number", ["description"] = "可选服务器 ID" } + } + } + } + }, + new JsonObject + { + ["type"] = "function", + ["function"] = new JsonObject + { + ["name"] = "list_server_mods_plugins", + ["description"] = "专门列出指定服务器实例目录下的 `mods/` 或 `plugins/` 文件夹中的所有模组/插件文件(包括启用状态 `.jar` 和已禁用 `.jar.disabled`)、文件大小及修改时间。", + ["parameters"] = new JsonObject + { + ["type"] = "object", + ["properties"] = new JsonObject + { + ["server_id"] = new JsonObject { ["type"] = "number", ["description"] = "服务器 ID,未指定时结合【当前用户界面上下文】的主机 ID 或最新服务器" }, + ["folder_type"] = new JsonObject { ["type"] = "string", ["description"] = "分类,取值为 'mods', 'plugins', 'both',默认为 'both'" } + } + } + } + } + }; + } + + private async Task ExecuteToolAsync(string name, string argsJson, Func onToolExecuted, bool assumeConfirmed = false) + { + try + { + try + { + await onToolExecuted(name, new { status = "executing", tool = name }); + } + catch { } + + var args = JsonNode.Parse(argsJson)?.AsObject(); + + switch (name) + { + case "update_instance_settings": + { + int targetId = 0; + if (args != null && args.ContainsKey("server_id") && args["server_id"] != null) + { + int.TryParse(args["server_id"].ToString(), out targetId); + } + + var serverList = IConfigBase.ServerList.GetServerList(); + var targetServer = targetId > 0 + ? serverList.FirstOrDefault(s => s.ID == targetId) + : serverList.OrderByDescending(s => s.ID).FirstOrDefault(); + + if (targetServer == null) + { + return "[ERROR] 未找到目标服务器实例。"; + } + + if (args != null && args.ContainsKey("java") && args["java"] != null) + { + string rawJava = args["java"].ToString().Trim(); + + if (rawJava.Equals("suitable", StringComparison.OrdinalIgnoreCase) || + rawJava.Equals("auto", StringComparison.OrdinalIgnoreCase) || + rawJava.Contains("合适") || rawJava.Contains("推荐")) + { + string versionHint = !string.IsNullOrEmpty(targetServer.Name) ? targetServer.Name : targetServer.Core; + int recMajor = GetRecommendedJavaMajor(versionHint); + rawJava = $"MSLX://Java/{recMajor}"; + } + else + { + var match = Regex.Match(rawJava, @"\d+"); + if (match.Success) + { + rawJava = $"MSLX://Java/{match.Value}"; + } + } + + targetServer.Java = rawJava; + } + + if (args != null && args.ContainsKey("name") && args["name"] != null) + { + targetServer.Name = args["name"].ToString(); + } + if (args != null && args.ContainsKey("max_m") && args["max_m"] != null) + { + if (int.TryParse(args["max_m"].ToString(), out int maxM)) + { + targetServer.MaxM = maxM; + } + } + if (args != null && args.ContainsKey("min_m") && args["min_m"] != null) + { + if (int.TryParse(args["min_m"].ToString(), out int minM)) + { + targetServer.MinM = minM; + } + } + if (args != null && args.ContainsKey("core") && args["core"] != null) + { + targetServer.Core = args["core"].ToString(); + } + if (args != null && args.ContainsKey("args") && args["args"] != null) + { + targetServer.Args = args["args"].ToString(); + } + if (args != null && args.ContainsKey("auto_restart") && args["auto_restart"] != null) + { + targetServer.AutoRestart = args["auto_restart"].GetValue(); + } + + IConfigBase.ServerList.UpdateServer(targetServer); + + var resData = new + { + serverId = targetServer.ID, + name = targetServer.Name, + java = targetServer.Java, + maxM = targetServer.MaxM, + minM = targetServer.MinM, + core = targetServer.Core + }; + await onToolExecuted("update_instance_settings", resData); + return $"[SUCCESS] 服务器 (ID: {targetServer.ID}, 名称: {targetServer.Name}) 的 MSLX 实例设置更新成功!绑定的 Java 已根据 MC 规则准确切换为 `{targetServer.Java}`,分配内存: {targetServer.MinM}M ~ {targetServer.MaxM}M!"; + } + + case "list_server_files": + { + int targetId = 0; + if (args != null && args.ContainsKey("server_id") && args["server_id"] != null) + { + int.TryParse(args["server_id"].ToString(), out targetId); + } + string dirPath = args?["dir_path"]?.ToString() ?? ""; + + var serverList = IConfigBase.ServerList.GetServerList(); + var targetServer = targetId > 0 + ? serverList.FirstOrDefault(s => s.ID == targetId) + : serverList.OrderByDescending(s => s.ID).FirstOrDefault(); + + if (targetServer == null) + { + return "[ERROR] 未找到目标服务器实例。"; + } + + string serverDir = targetServer.Base; + if (string.IsNullOrEmpty(serverDir) || !Directory.Exists(serverDir)) + { + serverDir = Path.Combine(IConfigBase.GetAppDataPath(), "Servers", targetServer.ID.ToString()); + } + + string safeBaseDir = Path.GetFullPath(serverDir); + string targetDirPath = Path.GetFullPath(Path.Combine(safeBaseDir, dirPath)); + + if (!targetDirPath.StartsWith(safeBaseDir, StringComparison.OrdinalIgnoreCase)) + { + return "[ERROR] 越权访问:不允许查看服务器实例目录之外的文件。"; + } + + if (!Directory.Exists(targetDirPath)) + { + return $"[ERROR] 目录不存在: {dirPath}"; + } + + var entries = Directory.GetFileSystemEntries(targetDirPath) + .Select(p => new + { + name = Path.GetFileName(p), + isDir = Directory.Exists(p), + size = Directory.Exists(p) ? 0 : new FileInfo(p).Length, + lastModified = File.GetLastWriteTime(p).ToString("yyyy-MM-dd HH:mm:ss") + }).ToList(); + + var listResult = new { serverId = targetServer.ID, dirPath = dirPath, count = entries.Count, items = entries }; + await onToolExecuted("list_server_files", listResult); + return $"[SUCCESS] 目录 `{dirPath}` 文件列表:\n" + JsonSerializer.Serialize(entries); + } + + case "query_creation_status": + { + int targetId = 0; + if (args != null && args.ContainsKey("server_id") && args["server_id"] != null) + { + int.TryParse(args["server_id"].ToString(), out targetId); + } + + if (targetId == 0) return "[ERROR] 请提供有效的 server_id"; + + if (_memoryCache.TryGetValue(targetId.ToString(), out CacheableStatus? status) && status != null) + { + bool isSuccess = status.Progress >= 100 || status.Message.Contains("完成"); + bool isFailure = status.Progress < 0 || status.Message.Contains("失败") || status.Message.Contains("中断") || status.Message.Contains("错误"); + + await onToolExecuted("query_creation_status", status); + + if (isFailure) + { + return $"[ERROR] 服务器 {targetId} 安装部署失败: {status.Message}"; + } + else if (isSuccess) + { + return $"[SUCCESS] 服务器 {targetId} 建服全流程已成功完成!目前可以安全进行配置文件修改和启动控制。"; + } + else + { + return $"[IN_PROGRESS] 服务器 {targetId} 正在后台安装中 (进度: {status.Progress:F1}%, 状态: {status.Message})。"; + } + } + + return $"[INFO] 未找到服务器 {targetId} 的实时安装缓存,可能已安装完成或实例不存在。"; + } + + case "query_java_environments": + { + using var scope = _scopeFactory.CreateScope(); + var javaScanner = scope.ServiceProvider.GetRequiredService(); + var list = await javaScanner.ScanJavaAsync(false); + + var resList = list.Select(j => { + string major = GetJavaMajorVersion(j.Version); + return new + { + version = j.Version, + majorVersion = major, + vendor = j.Vendor, + path = j.Path, + mslxIdentifier = $"MSLX://Java/{major}" + }; + }).ToList(); + + await onToolExecuted("query_java_environments", resList); + return $"[SUCCESS] 扫描到的 Java 环境列表:\n" + JsonSerializer.Serialize(resList); + } + + case "query_available_server_cores": + { + string? coreName = args?["core_name"]?.ToString()?.Trim()?.ToLower(); + if (string.IsNullOrEmpty(coreName)) + { + var (succ, data, msg) = await MSLApi.GetDataAsync("/mirrors"); + var cleanData = ConvertJTokenToJsonNode(data); + await onToolExecuted("query_available_server_cores", new { query = "all_cores", success = succ, data = cleanData }); + return succ && cleanData != null + ? $"[SUCCESS] MSL 镜像站全量支持的核心类型列表:\n{cleanData.ToJsonString()}" + : $"[ERROR] 查询镜像核心分类失败: {msg}"; + } + else + { + var (succ, data, msg) = await MSLApi.GetDataAsync($"/mirrors/{coreName}"); + var cleanData = ConvertJTokenToJsonNode(data); + await onToolExecuted("query_available_server_cores", new { query = coreName, success = succ, data = cleanData }); + return succ && cleanData != null + ? $"[SUCCESS] 核心 [{coreName}] 支持的游戏版本列表:\n{cleanData.ToJsonString()}" + : $"[ERROR] 查询核心 [{coreName}] 支持版本失败: {msg}"; + } + } + + case "create_mc_server": + { + string type = args?["server_type"]?.ToString() ?? "paper"; + string version = args?["mc_version"]?.ToString() ?? "1.20.2"; + string serverName = args?["server_name"]?.ToString() ?? $"{type.ToUpper()}-{version}"; + string specifiedJava = args?["java"]?.ToString() ?? ""; + + int maxM = 2048; + if (args != null && args.ContainsKey("max_m") && args["max_m"] != null) + int.TryParse(args["max_m"].ToString(), out maxM); + + int minM = 1024; + if (args != null && args.ContainsKey("min_m") && args["min_m"] != null) + int.TryParse(args["min_m"].ToString(), out minM); + + string jvmArgs = args?["args"]?.ToString() ?? ""; + bool ignoreEula = true; + if (args != null && args.ContainsKey("ignore_eula") && args["ignore_eula"] != null) + bool.TryParse(args["ignore_eula"].ToString(), out ignoreEula); + + string serverPort = args?["server_port"]?.ToString() ?? "25565"; + string customPath = args?["path"]?.ToString() ?? ""; + + // 整合包 / 压缩包模式参数 + string packageLocalPath = args?["package_local_path"]?.ToString() ?? ""; + string packageUrl = args?["package_url"]?.ToString() ?? ""; + + // MCDR 模式参数 + bool isMcdr = false; + if (args != null && args.ContainsKey("mcdr") && args["mcdr"] != null) + bool.TryParse(args["mcdr"].ToString(), out isMcdr); + + string mcdrPython = args?["mcdr_python"]?.ToString() ?? "python"; + string mcdrHandler = args?["mcdr_handler"]?.ToString() ?? ""; + bool mcdrInstall = true; + if (args != null && args.ContainsKey("mcdr_install") && args["mcdr_install"] != null) + bool.TryParse(args["mcdr_install"].ToString(), out mcdrInstall); + + string mcdrPipMirror = args?["mcdr_pip_mirror"]?.ToString() ?? ""; + + // 自定义核心 / 核心直链参数 + string customCoreUrl = args?["core_url"]?.ToString() ?? ""; + string customCoreFileName = args?["core_filename"]?.ToString() ?? ""; + + int recJavaMajor = GetRecommendedJavaMajor(version); + + string selectedJava = specifiedJava; + if (string.IsNullOrWhiteSpace(selectedJava) || selectedJava.Equals("auto", StringComparison.OrdinalIgnoreCase)) + { + using var scope = _scopeFactory.CreateScope(); + var javaScanner = scope.ServiceProvider.GetRequiredService(); + var scannedJavaList = await javaScanner.ScanJavaAsync(false); + + if (scannedJavaList != null && scannedJavaList.Count > 0) + { + var parsedJavaList = scannedJavaList + .Select(j => new { Java = j, Major = int.TryParse(GetJavaMajorVersion(j.Version), out int m) ? m : 8 }) + .ToList(); + + var exactMatch = parsedJavaList.FirstOrDefault(x => x.Major == recJavaMajor); + if (exactMatch != null) + { + selectedJava = $"MSLX://Java/{exactMatch.Major}"; + } + else + { + var suitableJava = parsedJavaList + .Where(x => x.Major >= recJavaMajor) + .OrderBy(x => x.Major) + .FirstOrDefault() ?? parsedJavaList.OrderByDescending(x => x.Major).FirstOrDefault(); + + if (suitableJava != null) + { + selectedJava = $"MSLX://Java/{suitableJava.Major}"; + } + } + } + } + + if (string.IsNullOrWhiteSpace(selectedJava)) + { + selectedJava = "auto"; + } + + string? coreUrl = string.IsNullOrWhiteSpace(customCoreUrl) ? null : customCoreUrl; + string? coreSha256 = null; + string coreFileName = string.IsNullOrWhiteSpace(customCoreFileName) ? $"{type}-{version}.jar" : customCoreFileName; + + if (string.IsNullOrWhiteSpace(coreUrl) && string.IsNullOrWhiteSpace(packageLocalPath) && string.IsNullOrWhiteSpace(packageUrl)) + { + try + { + var (apiSuccess, apiData, _) = await MSLApi.GetDataAsync($"/download/server/{type.ToLower()}/{version}?build=latest"); + if (apiSuccess && apiData is JToken token) + { + coreUrl = token["url"]?.ToString(); + coreSha256 = token["sha256"]?.ToString(); + if (token["name"] != null && !string.IsNullOrEmpty(token["name"].ToString())) + { + coreFileName = token["name"].ToString(); + } + } + } + catch (Exception ex) + { + _logger.LogWarning(ex, "向镜像站查询核心下载地址失败: {Type} {Version}", type, version); + } + } + + var serverId = IConfigBase.ServerList.GenerateServerId(); + var createRequest = new CreateServerRequest + { + name = serverName, + core = coreFileName, + coreUrl = coreUrl, + coreSha256 = coreSha256, + java = selectedJava, + maxM = maxM, + minM = minM, + args = string.IsNullOrWhiteSpace(jvmArgs) ? null : jvmArgs, + ignoreEula = ignoreEula, + path = string.IsNullOrWhiteSpace(customPath) ? null : customPath, + DockerPorts = $"{serverPort}:{serverPort}", + packageLocalPath = string.IsNullOrWhiteSpace(packageLocalPath) ? null : packageLocalPath, + packageUrl = string.IsNullOrWhiteSpace(packageUrl) ? null : packageUrl, + mcdr = isMcdr, + mcdrPython = isMcdr ? mcdrPython : null, + mcdrHandler = isMcdr && !string.IsNullOrWhiteSpace(mcdrHandler) ? mcdrHandler : null, + mcdrInstall = mcdrInstall, + mcdrPipMirror = string.IsNullOrWhiteSpace(mcdrPipMirror) ? null : mcdrPipMirror + }; + + var task = new CreateServerTask + { + ServerId = serverId.ToString(), + Request = createRequest + }; + + await _createTaskQueue.QueueTaskAsync(task); + + int maxWaitMs = 300000; + int intervalMs = 1000; + CacheableStatus? finalStatus = null; + + while (maxWaitMs > 0) + { + await Task.Delay(intervalMs); + maxWaitMs -= intervalMs; + + if (_memoryCache.TryGetValue(serverId.ToString(), out CacheableStatus? cacheStatus) && cacheStatus != null) + { + finalStatus = cacheStatus; + if (cacheStatus.Progress >= 100 || cacheStatus.Message.Contains("完成") || cacheStatus.Message.Contains("失败") || cacheStatus.Progress < 0) + { + break; + } + } + } + + bool isSuccess = finalStatus != null && (finalStatus.Progress >= 100 || finalStatus.Message.Contains("完成")); + bool isFailure = finalStatus != null && (finalStatus.Progress < 0 || finalStatus.Message.Contains("失败") || finalStatus.Message.Contains("中断") || finalStatus.Message.Contains("错误")); + + var resData = new + { + serverId = serverId, + name = serverName, + type = type, + version = version, + recommendedJavaMajor = recJavaMajor, + java = selectedJava, + status = finalStatus?.Message ?? "部署完成", + progress = finalStatus?.Progress ?? 100 + }; + await onToolExecuted("create_mc_server", resData); + + if (isFailure) + { + return $"[ERROR] 服务器 (ID: {serverId}) 后台部署失败: {finalStatus?.Message ?? "部署过程发生错误"}"; + } + else + { + return $"[SUCCESS] 服务器 (ID: {serverId}, 名称: {serverName}) 核心包部署安装已全量成功完成 (自动为 MC {version} 推荐并绑定 Java {recJavaMajor})!请立刻接着调用 write_server_file 或 update_server_config 为用户修改配置文件。"; + } + } + + case "read_server_file": + { + int targetId = 0; + if (args != null && args.ContainsKey("server_id") && args["server_id"] != null) + { + int.TryParse(args["server_id"].ToString(), out targetId); + } + string filePath = args?["file_path"]?.ToString() ?? ""; + + if (string.IsNullOrWhiteSpace(filePath)) + { + return "[ERROR] file_path 不能为空。"; + } + + var serverList = IConfigBase.ServerList.GetServerList(); + var targetServer = targetId > 0 + ? serverList.FirstOrDefault(s => s.ID == targetId) + : serverList.OrderByDescending(s => s.ID).FirstOrDefault(); + + if (targetServer == null) + { + return "[ERROR] 未找到目标服务器实例。"; + } + + string serverDir = targetServer.Base; + if (string.IsNullOrEmpty(serverDir) || !Directory.Exists(serverDir)) + { + serverDir = Path.Combine(IConfigBase.GetAppDataPath(), "Servers", targetServer.ID.ToString()); + } + + string safeBaseDir = Path.GetFullPath(serverDir); + string targetFilePath = Path.GetFullPath(Path.Combine(safeBaseDir, filePath)); + + if (!targetFilePath.StartsWith(safeBaseDir, StringComparison.OrdinalIgnoreCase)) + { + return "[ERROR] 越权访问:不允许读取服务器实例目录之外的文件。"; + } + + if (!File.Exists(targetFilePath)) + { + return $"[ERROR] 文件不存在: {filePath}"; + } + + string content; + using (var fs = new FileStream(targetFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + using (var sr = new StreamReader(fs, Encoding.UTF8)) + { + if (fs.Length > 200 * 1024) + { + fs.Seek(-200 * 1024, SeekOrigin.End); + sr.DiscardBufferedData(); + content = "……(日志文件过大,已自动截取最新末尾日志)……\n" + await sr.ReadToEndAsync(); + } + else + { + content = await sr.ReadToEndAsync(); + } + } + + var readResult = new { serverId = targetServer.ID, filePath = filePath, length = content.Length }; + await onToolExecuted("read_server_file", readResult); + return $"[SUCCESS] 读取文件 {filePath} 成功:\n```\n{content}\n```"; + } + + case "write_server_file": + { + int targetId = 0; + if (args != null && args.ContainsKey("server_id") && args["server_id"] != null) + { + int.TryParse(args["server_id"].ToString(), out targetId); + } + string filePath = args?["file_path"]?.ToString() ?? ""; + string content = args?["content"]?.ToString() ?? ""; + bool isConfirmed = assumeConfirmed; + + if (string.IsNullOrWhiteSpace(filePath)) + { + return "[ERROR] file_path 不能为空。"; + } + + var serverList = IConfigBase.ServerList.GetServerList(); + var targetServer = targetId > 0 + ? serverList.FirstOrDefault(s => s.ID == targetId) + : serverList.OrderByDescending(s => s.ID).FirstOrDefault(); + + if (targetServer == null) + { + return "[ERROR] 未找到目标服务器实例。"; + } + + string serverDir = targetServer.Base; + if (string.IsNullOrEmpty(serverDir) || !Directory.Exists(serverDir)) + { + serverDir = Path.Combine(IConfigBase.GetAppDataPath(), "Servers", targetServer.ID.ToString()); + Directory.CreateDirectory(serverDir); + } + + string safeBaseDir = Path.GetFullPath(serverDir); + string targetFilePath = Path.GetFullPath(Path.Combine(safeBaseDir, filePath)); + + if (!targetFilePath.StartsWith(safeBaseDir, StringComparison.OrdinalIgnoreCase)) + { + return "[ERROR] 越权操作:不允许写文件到服务器实例目录之外。"; + } + + bool exists = File.Exists(targetFilePath); + if (exists && !isConfirmed) + { + string confirmationId = CreateOrGetPendingOperation("write_server_file", argsJson, targetServer.ID, filePath, "edit"); + var needConfirmData = new { serverId = targetServer.ID, filePath = filePath, action = "edit", requiresConfirmation = true, confirmed = false, confirmationId = confirmationId, content = content }; + await onToolExecuted("write_server_file", needConfirmData); + return $"[PENDING_CONFIRMATION] 警告:准备覆盖编辑已有文件 `{filePath}`。操作已挂起,前端已弹出确认按钮,用户点击确认授权后系统将自动执行写入,无需再次调用工具。"; + } + + string? parentDir = Path.GetDirectoryName(targetFilePath); + if (!string.IsNullOrEmpty(parentDir) && !Directory.Exists(parentDir)) + { + Directory.CreateDirectory(parentDir); + } + + await File.WriteAllTextAsync(targetFilePath, content); + var writeResult = new { serverId = targetServer.ID, filePath = filePath, contentLength = content.Length, confirmed = true }; + await onToolExecuted("write_server_file", writeResult); + return $"[SUCCESS] 成功写入/更新服务器 {targetServer.ID} 的文件 `{filePath}`!"; + } + + case "delete_server_file": + { + int targetId = 0; + if (args != null && args.ContainsKey("server_id") && args["server_id"] != null) + { + int.TryParse(args["server_id"].ToString(), out targetId); + } + string filePath = args?["file_path"]?.ToString() ?? ""; + bool isConfirmed = assumeConfirmed; + + if (string.IsNullOrWhiteSpace(filePath)) + { + return "[ERROR] file_path 不能为空。"; + } + + var serverList = IConfigBase.ServerList.GetServerList(); + var targetServer = targetId > 0 + ? serverList.FirstOrDefault(s => s.ID == targetId) + : serverList.OrderByDescending(s => s.ID).FirstOrDefault(); + + if (targetServer == null) + { + return "[ERROR] 未找到目标服务器实例。"; + } + + string serverDir = targetServer.Base; + if (string.IsNullOrEmpty(serverDir) || !Directory.Exists(serverDir)) + { + serverDir = Path.Combine(IConfigBase.GetAppDataPath(), "Servers", targetServer.ID.ToString()); + } + + string safeBaseDir = Path.GetFullPath(serverDir); + string targetPath = Path.GetFullPath(Path.Combine(safeBaseDir, filePath)); + + if (!targetPath.StartsWith(safeBaseDir, StringComparison.OrdinalIgnoreCase)) + { + return "[ERROR] 越权操作:不允许删除服务器实例目录之外的文件。"; + } + + if (!isConfirmed) + { + string confirmationId = CreateOrGetPendingOperation("delete_server_file", argsJson, targetServer.ID, filePath, "delete"); + var confirmData = new { serverId = targetServer.ID, filePath = filePath, action = "delete", requiresConfirmation = true, confirmed = false, confirmationId = confirmationId }; + await onToolExecuted("delete_server_file", confirmData); + return $"[PENDING_CONFIRMATION] 高风险警告:已申请删除服务器文件/目录 `{filePath}`!操作已挂起,前端已弹出确认按钮,用户点击确认授权后系统将自动执行删除,无需再次调用工具。"; + } + + if (File.Exists(targetPath)) + { + File.Delete(targetPath); + } + else if (Directory.Exists(targetPath)) + { + Directory.Delete(targetPath, true); + } + else + { + return $"[ERROR] 要删除的文件/目录不存在: {filePath}"; + } + + var deleteResult = new { serverId = targetServer.ID, filePath = filePath, action = "delete", confirmed = true }; + await onToolExecuted("delete_server_file", deleteResult); + return $"[SUCCESS] 已成功彻底删除服务器 {targetServer.ID} 的 `{filePath}`!"; + } + + case "update_server_config": + { + int targetId = 0; + if (args != null && args.ContainsKey("server_id") && args["server_id"] != null) + { + int.TryParse(args["server_id"].ToString(), out targetId); + } + + var serverList = IConfigBase.ServerList.GetServerList(); + var targetServer = targetId > 0 + ? serverList.FirstOrDefault(s => s.ID == targetId) + : serverList.OrderByDescending(s => s.ID).FirstOrDefault(); + + if (targetServer == null) + { + return "[ERROR] 未找到目标服务器实例。"; + } + + string serverDir = targetServer.Base; + if (string.IsNullOrEmpty(serverDir) || !Directory.Exists(serverDir)) + { + serverDir = Path.Combine(IConfigBase.GetAppDataPath(), "Servers", targetServer.ID.ToString()); + Directory.CreateDirectory(serverDir); + } + + string propsPath = Path.Combine(serverDir, "server.properties"); + var propsDict = new Dictionary(); + if (File.Exists(propsPath)) + { + using (var fs = new FileStream(propsPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + using (var sr = new StreamReader(fs, Encoding.UTF8)) + { + string? line; + while ((line = await sr.ReadLineAsync()) != null) + { + if (!string.IsNullOrWhiteSpace(line) && !line.StartsWith("#") && line.Contains('=')) + { + var parts = line.Split('=', 2); + propsDict[parts[0].Trim()] = parts[1].Trim(); + } + } + } + } + + if (args != null && args.ContainsKey("online_mode") && args["online_mode"] != null) + { + propsDict["online-mode"] = args["online_mode"].GetValue() ? "true" : "false"; + } + if (args != null && args.ContainsKey("pvp") && args["pvp"] != null) + { + propsDict["pvp"] = args["pvp"].GetValue() ? "true" : "false"; + } + if (args != null && args.ContainsKey("max_players") && args["max_players"] != null) + { + propsDict["max-players"] = args["max_players"].ToString(); + } + if (args != null && args.ContainsKey("server_port") && args["server_port"] != null) + { + propsDict["server-port"] = args["server_port"].ToString(); + } + if (args != null && args.ContainsKey("motd") && args["motd"] != null) + { + propsDict["motd"] = args["motd"].ToString(); + } + + var sb = new StringBuilder("#Minecraft server properties\n"); + foreach (var kvp in propsDict) + { + sb.AppendLine($"{kvp.Key}={kvp.Value}"); + } + File.WriteAllText(propsPath, sb.ToString()); + + var updateResult = new { serverId = targetServer.ID, serverName = targetServer.Name, properties = propsDict }; + await onToolExecuted("update_server_config", updateResult); + return $"[SUCCESS] 服务器 (ID: {targetServer.ID}, 名称: {targetServer.Name}) 的 `server.properties` 配置更新成功!"; + } + + case "control_server": + { + uint serverId = 0; + if (args != null && args.ContainsKey("server_id") && args["server_id"] != null) + { + uint.TryParse(args["server_id"].ToString(), out serverId); + } + string action = args?["action"]?.ToString()?.ToLower() ?? "start"; + + var serverList = IConfigBase.ServerList.GetServerList(); + if (serverId == 0 && serverList.Count > 0) + { + serverId = (uint)serverList.OrderByDescending(s => s.ID).First().ID; + } + + if (serverId == 0) return "[ERROR] 未指定有效的 server_id,且当前没有任何服务器实例。"; + + if (action == "start") + { + var (succ, msg) = _mcServerService.StartServer(serverId); + await onToolExecuted("control_server", new { serverId, action, succ, msg }); + return succ ? $"[SUCCESS] 服务器 {serverId} 启动指令已发送。" : $"[ERROR] 启动服务器 {serverId} 失败: {msg}"; + } + else if (action == "stop") + { + bool stopped = _mcServerService.StopServer(serverId); + await onToolExecuted("control_server", new { serverId, action, stopped }); + return stopped ? $"[SUCCESS] 服务器 {serverId} 停止指令已发送。" : $"[ERROR] 停止服务器 {serverId} 失败。"; + } + else if (action == "restart") + { + var (succ, msg) = await _mcServerService.RestartServer(serverId); + await onToolExecuted("control_server", new { serverId, action, succ, msg }); + return succ ? $"[SUCCESS] 服务器 {serverId} 重启指令已完成。" : $"[ERROR] 重启服务器 {serverId} 失败: {msg}"; + } + + return "[ERROR] 未知的控制动作。"; + } + + case "query_server_status": + { + var servers = IConfigBase.ServerList.GetServerList(); + var list = new List(); + foreach (var s in servers) + { + var (status, desc) = _mcServerService.GetServerStatus((uint)s.ID); + list.Add(new + { + id = s.ID, + name = s.Name, + core = s.Core, + status = desc + }); + } + await onToolExecuted("query_server_status", list); + return $"[SUCCESS] 当前服务器列表与状态:\n" + JsonSerializer.Serialize(list); + } + + case "read_server_log": + { + int targetId = 0; + if (args != null && args.ContainsKey("server_id") && args["server_id"] != null) + int.TryParse(args["server_id"].ToString(), out targetId); + + string logType = (args != null && args.ContainsKey("log_type") && args["log_type"] != null) + ? args["log_type"].ToString().Trim() + : "latest"; + + int tailLines = 100; + if (args != null && args.ContainsKey("tail_lines") && args["tail_lines"] != null) + int.TryParse(args["tail_lines"].ToString(), out tailLines); + + var serverList = IConfigBase.ServerList.GetServerList(); + var targetServer = targetId > 0 + ? serverList.FirstOrDefault(s => s.ID == targetId) + : serverList.OrderByDescending(s => s.ID).FirstOrDefault(); + + if (targetServer == null) return "[ERROR] 未找到目标服务器实例。"; + + string serverDir = targetServer.Base; + if (string.IsNullOrEmpty(serverDir) || !Directory.Exists(serverDir)) + { + serverDir = Path.Combine(IConfigBase.GetAppDataPath(), "Servers", targetServer.ID.ToString()); + } + + string targetFilePath = ""; + if (logType.Equals("crash_report", StringComparison.OrdinalIgnoreCase)) + { + string crashDir = Path.Combine(serverDir, "crash-reports"); + if (!Directory.Exists(crashDir)) return "[INFO] 未找到 crash-reports 崩溃日志目录,服务器暂无崩溃记录。"; + + var latestCrashFile = new DirectoryInfo(crashDir) + .GetFiles("*.txt") + .OrderByDescending(f => f.LastWriteTime) + .FirstOrDefault(); + + if (latestCrashFile == null) return "[INFO] crash-reports 目录下暂无崩溃文件。"; + targetFilePath = latestCrashFile.FullName; + } + else + { + targetFilePath = Path.Combine(serverDir, "logs", "latest.log"); + if (!File.Exists(targetFilePath)) return "[INFO] 未找到 logs/latest.log 运行日志文件。"; + } + + try + { + using var fs = new FileStream(targetFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + using var reader = new StreamReader(fs, Encoding.UTF8); + var lines = new List(); + string? line; + while ((line = await reader.ReadLineAsync()) != null) + { + lines.Add(line); + } + + var selectedLines = lines.TakeLast(Math.Min(tailLines, 300)).ToList(); + var resultText = string.Join("\n", selectedLines); + var resData = new { file = Path.GetFileName(targetFilePath), linesCount = selectedLines.Count, content = resultText }; + await onToolExecuted("read_server_log", resData); + return $"[SUCCESS] 成功提取日志 {Path.GetFileName(targetFilePath)} 末尾 {selectedLines.Count} 行内容:\n\n```log\n{resultText}\n```"; + } + catch (Exception ex) + { + return $"[ERROR] 读取日志文件失败: {ex.Message}"; + } + } + + case "copy_server_file": + { + int targetId = 0; + if (args != null && args.ContainsKey("server_id") && args["server_id"] != null) + int.TryParse(args["server_id"].ToString(), out targetId); + + string srcRel = args?["source_path"]?.ToString().Trim() ?? ""; + string dstRel = args?["destination_path"]?.ToString().Trim() ?? ""; + + if (string.IsNullOrWhiteSpace(srcRel) || string.IsNullOrWhiteSpace(dstRel)) + return "[ERROR] 参数缺失:source_path 和 destination_path 不能为空。"; + + var serverList = IConfigBase.ServerList.GetServerList(); + var targetServer = targetId > 0 + ? serverList.FirstOrDefault(s => s.ID == targetId) + : serverList.OrderByDescending(s => s.ID).FirstOrDefault(); + + if (targetServer == null) return "[ERROR] 未找到目标服务器实例。"; + + string serverDir = targetServer.Base; + if (string.IsNullOrEmpty(serverDir) || !Directory.Exists(serverDir)) + { + serverDir = Path.Combine(IConfigBase.GetAppDataPath(), "Servers", targetServer.ID.ToString()); + } + + string srcPath = Path.GetFullPath(Path.Combine(serverDir, srcRel)); + string dstPath = Path.GetFullPath(Path.Combine(serverDir, dstRel)); + + if (!srcPath.StartsWith(serverDir, StringComparison.OrdinalIgnoreCase) || + !dstPath.StartsWith(serverDir, StringComparison.OrdinalIgnoreCase)) + return "[ERROR] 越权访问:文件路径超出目标服务器根目录。"; + + if (File.Exists(srcPath)) + { + Directory.CreateDirectory(Path.GetDirectoryName(dstPath)!); + File.Copy(srcPath, dstPath, overwrite: true); + var resData = new { action = "copy", source = srcRel, destination = dstRel }; + await onToolExecuted("copy_server_file", resData); + return $"[SUCCESS] 成功复制文件: {srcRel} ➔ {dstRel}"; + } + else if (Directory.Exists(srcPath)) + { + CopyDirectoryRecursively(srcPath, dstPath); + var resData = new { action = "copy_dir", source = srcRel, destination = dstRel }; + await onToolExecuted("copy_server_file", resData); + return $"[SUCCESS] 成功复制目录: {srcRel} ➔ {dstRel}"; + } + return $"[ERROR] 源文件或目录不存在: {srcRel}"; + } + + case "move_server_file": + { + int targetId = 0; + if (args != null && args.ContainsKey("server_id") && args["server_id"] != null) + int.TryParse(args["server_id"].ToString(), out targetId); + + string srcRel = args?["source_path"]?.ToString().Trim() ?? ""; + string dstRel = args?["destination_path"]?.ToString().Trim() ?? ""; + + if (string.IsNullOrWhiteSpace(srcRel) || string.IsNullOrWhiteSpace(dstRel)) + return "[ERROR] 参数缺失:source_path 和 destination_path 不能为空。"; + + var serverList = IConfigBase.ServerList.GetServerList(); + var targetServer = targetId > 0 + ? serverList.FirstOrDefault(s => s.ID == targetId) + : serverList.OrderByDescending(s => s.ID).FirstOrDefault(); + + if (targetServer == null) return "[ERROR] 未找到目标服务器实例。"; + + string serverDir = targetServer.Base; + if (string.IsNullOrEmpty(serverDir) || !Directory.Exists(serverDir)) + { + serverDir = Path.Combine(IConfigBase.GetAppDataPath(), "Servers", targetServer.ID.ToString()); + } + + string srcPath = Path.GetFullPath(Path.Combine(serverDir, srcRel)); + string dstPath = Path.GetFullPath(Path.Combine(serverDir, dstRel)); + + if (!srcPath.StartsWith(serverDir, StringComparison.OrdinalIgnoreCase) || + !dstPath.StartsWith(serverDir, StringComparison.OrdinalIgnoreCase)) + return "[ERROR] 越权访问:文件路径超出目标服务器根目录。"; + + if (File.Exists(srcPath)) + { + Directory.CreateDirectory(Path.GetDirectoryName(dstPath)!); + if (File.Exists(dstPath)) File.Delete(dstPath); + File.Move(srcPath, dstPath); + var resData = new { action = "move", source = srcRel, destination = dstRel }; + await onToolExecuted("move_server_file", resData); + return $"[SUCCESS] 成功移动/重命名文件: {srcRel} ➔ {dstRel}"; + } + else if (Directory.Exists(srcPath)) + { + if (Directory.Exists(dstPath)) Directory.Delete(dstPath, true); + Directory.Move(srcPath, dstPath); + var resData = new { action = "move_dir", source = srcRel, destination = dstRel }; + await onToolExecuted("move_server_file", resData); + return $"[SUCCESS] 成功移动/重命名目录: {srcRel} ➔ {dstRel}"; + } + return $"[ERROR] 源文件或目录不存在: {srcRel}"; + } + + case "query_system_metrics": + { + int targetId = 0; + if (args != null && args.ContainsKey("server_id") && args["server_id"] != null) + int.TryParse(args["server_id"].ToString(), out targetId); + + var serverList = IConfigBase.ServerList.GetServerList(); + var targetServer = targetId > 0 + ? serverList.FirstOrDefault(s => s.ID == targetId) + : serverList.OrderByDescending(s => s.ID).FirstOrDefault(); + + var gcMemoryMb = GC.GetTotalMemory(false) / 1024 / 1024; + var process = System.Diagnostics.Process.GetCurrentProcess(); + var processWorkingSetMb = process.WorkingSet64 / 1024 / 1024; + + var resData = new + { + systemMemoryMb = processWorkingSetMb, + gcMemoryMb = gcMemoryMb, + targetServerId = targetServer?.ID, + targetServerName = targetServer?.Name, + targetServerJava = targetServer?.Java, + targetServerMemory = targetServer?.MaxM + }; + + await onToolExecuted("query_system_metrics", resData); + return $"[SUCCESS] 系统性能状态:宿主机 Daemon 进程内存使用 {processWorkingSetMb}MB (GC堆: {gcMemoryMb}MB);当前服务器 #{targetServer?.ID ?? 0} [{targetServer?.Name}] 设定内存: {targetServer?.MaxM?.ToString() ?? "默认"}MB,绑定Java: {targetServer?.Java ?? "默认"}。"; + } + + case "list_server_mods_plugins": + { + int targetId = 0; + if (args != null && args.ContainsKey("server_id") && args["server_id"] != null) + int.TryParse(args["server_id"].ToString(), out targetId); + + string folderType = args?["folder_type"]?.ToString().Trim() ?? "both"; + + var serverList = IConfigBase.ServerList.GetServerList(); + var targetServer = targetId > 0 + ? serverList.FirstOrDefault(s => s.ID == targetId) + : serverList.OrderByDescending(s => s.ID).FirstOrDefault(); + + if (targetServer == null) return "[ERROR] 未找到目标服务器实例。"; + + string serverDir = targetServer.Base; + if (string.IsNullOrEmpty(serverDir) || !Directory.Exists(serverDir)) + { + serverDir = Path.Combine(IConfigBase.GetAppDataPath(), "Servers", targetServer.ID.ToString()); + } + + var mods = new List(); + var plugins = new List(); + + if (folderType == "mods" || folderType == "both") + { + string modsDir = Path.Combine(serverDir, "mods"); + if (Directory.Exists(modsDir)) + { + var files = new DirectoryInfo(modsDir).GetFiles("*.*") + .Where(f => f.Extension.Equals(".jar", StringComparison.OrdinalIgnoreCase) || f.Extension.Equals(".disabled", StringComparison.OrdinalIgnoreCase) || f.Name.EndsWith(".jar.disabled", StringComparison.OrdinalIgnoreCase)) + .OrderBy(f => f.Name); + + foreach (var f in files) + { + bool isEnabled = f.Extension.Equals(".jar", StringComparison.OrdinalIgnoreCase); + mods.Add(new { name = f.Name, sizeKb = f.Length / 1024, enabled = isEnabled, lastModified = f.LastWriteTime.ToString("yyyy-MM-dd HH:mm") }); + } + } + } + + if (folderType == "plugins" || folderType == "both") + { + string pluginsDir = Path.Combine(serverDir, "plugins"); + if (Directory.Exists(pluginsDir)) + { + var files = new DirectoryInfo(pluginsDir).GetFiles("*.*") + .Where(f => f.Extension.Equals(".jar", StringComparison.OrdinalIgnoreCase) || f.Extension.Equals(".disabled", StringComparison.OrdinalIgnoreCase) || f.Name.EndsWith(".jar.disabled", StringComparison.OrdinalIgnoreCase)) + .OrderBy(f => f.Name); + + foreach (var f in files) + { + bool isEnabled = f.Extension.Equals(".jar", StringComparison.OrdinalIgnoreCase); + plugins.Add(new { name = f.Name, sizeKb = f.Length / 1024, enabled = isEnabled, lastModified = f.LastWriteTime.ToString("yyyy-MM-dd HH:mm") }); + } + } + } + + var resData = new { modsCount = mods.Count, pluginsCount = plugins.Count, mods = mods, plugins = plugins }; + await onToolExecuted("list_server_mods_plugins", resData); + return $"[SUCCESS] 服务器 #{targetServer.ID} Mod 数量: {mods.Count} 个, 插件数量: {plugins.Count} 个。" + + (mods.Count > 0 ? "\n\nMod 列表:\n" + string.Join("\n", mods.Select(m => $"- {((dynamic)m).name} ({(((dynamic)m).enabled ? "✅ 已启用" : "❌ 已禁用")}, {((dynamic)m).sizeKb}KB)")) : "") + + (plugins.Count > 0 ? "\n\n插件列表:\n" + string.Join("\n", plugins.Select(p => $"- {((dynamic)p).name} ({(((dynamic)p).enabled ? "✅ 已启用" : "❌ 已禁用")}, {((dynamic)p).sizeKb}KB)")) : ""); + } + + default: + return $"[ERROR] 未找到工具处理逻辑 {name}"; + } + } + catch (Exception ex) + { + _logger.LogError(ex, "执行 Tool {Name} 异常", name); + return $"[ERROR] 执行工具 {name} 失败: {ex.Message}"; + } + } + + private static string? GetStringOrNull(JsonNode? node) + { + return node != null && node.GetValueKind() == System.Text.Json.JsonValueKind.String + ? node.GetValue() + : null; + } + + private string CreateOrGetPendingOperation(string toolName, string argsJson, int serverId, string filePath, string action) + { + PurgeExpiredPendingOperations(); + + var existing = _pendingToolOperations.Values.FirstOrDefault(p => + p.ToolName == toolName && + p.ServerId == serverId && + string.Equals(p.FilePath, filePath, StringComparison.OrdinalIgnoreCase)); + + if (existing != null) + { + return existing.Id; + } + + var pending = new PendingToolOperation + { + Id = Guid.NewGuid().ToString("N"), + ToolName = toolName, + ArgsJson = argsJson, + ServerId = serverId, + FilePath = filePath, + Action = action, + CreatedAt = DateTime.UtcNow + }; + _pendingToolOperations[pending.Id] = pending; + return pending.Id; + } + + private void PurgeExpiredPendingOperations() + { + var cutoff = DateTime.UtcNow - PendingOperationTtl; + foreach (var kvp in _pendingToolOperations) + { + if (kvp.Value.CreatedAt < cutoff) + { + _pendingToolOperations.TryRemove(kvp.Key, out _); + } + } + } + + public async Task<(bool Success, string Message, object? Data)> ConfirmPendingToolAsync( + string confirmationId, + bool approved, + Func onToolExecuted) + { + if (string.IsNullOrWhiteSpace(confirmationId) || !_pendingToolOperations.TryRemove(confirmationId, out var pending)) + { + return (false, "确认凭证无效或已过期,请重新发起操作。", null); + } + + if (!approved) + { + await onToolExecuted(pending.ToolName, new + { + serverId = pending.ServerId, + filePath = pending.FilePath, + action = pending.Action, + confirmed = false, + rejected = true + }); + return (true, $"已拒绝{(pending.Action == "delete" ? "删除" : "覆盖写入")} `{pending.FilePath}`,操作已取消。", new { confirmed = false, rejected = true }); + } + + string result = await ExecuteToolAsync(pending.ToolName, pending.ArgsJson, onToolExecuted, assumeConfirmed: true); + return (true, result, new { confirmed = true }); + } + + private static void CopyDirectoryRecursively(string sourceDir, string targetDir) + { + Directory.CreateDirectory(targetDir); + foreach (var file in Directory.GetFiles(sourceDir)) + { + string dest = Path.Combine(targetDir, Path.GetFileName(file)); + File.Copy(file, dest, overwrite: true); + } + foreach (var dir in Directory.GetDirectories(sourceDir)) + { + string dest = Path.Combine(targetDir, Path.GetFileName(dir)); + CopyDirectoryRecursively(dir, dest); + } + } +} + +internal sealed class PendingToolOperation +{ + public required string Id { get; init; } + public required string ToolName { get; init; } + public required string ArgsJson { get; init; } + public required int ServerId { get; init; } + public required string FilePath { get; init; } + public required string Action { get; init; } + public required DateTime CreatedAt { get; init; } +} diff --git a/MSLX.Daemon/Skills/FileSafety.md b/MSLX.Daemon/Skills/FileSafety.md new file mode 100644 index 00000000..c5671117 --- /dev/null +++ b/MSLX.Daemon/Skills/FileSafety.md @@ -0,0 +1,5 @@ +# 敏感操作与前端 UI 二次确认规范 - 必须严格遵守 + +1. 当用户要求删除文件/目录(如删除主世界存档 `world`、旧插件、模组或日志)或覆盖修改文件内容时,【绝对禁止在文本回复中反问用户“你确认要删除/修改吗”】! +2. 必须【立即、果断调用 `delete_server_file` 或 `write_server_file` 工具】! +3. 系统后端会自动挂起该高风险请求,并在前端聊天卡片下方弹出交互式二次确认框与 `[确认授权删除/写入]` 按钮;用户点击确认后系统会自动完成真实操作并重新通知你,你无需重复调用工具! diff --git a/MSLX.Daemon/Skills/JavaMatrix.md b/MSLX.Daemon/Skills/JavaMatrix.md new file mode 100644 index 00000000..9010022d --- /dev/null +++ b/MSLX.Daemon/Skills/JavaMatrix.md @@ -0,0 +1,12 @@ +# MC 与 Java 版本对应准则 - 必须严格遵守 + +根据 Minecraft 游戏核心及服务端核心(Forge / NeoForge / Fabric / Paper / Spigot 等)的版本,严格匹配相应的 Java 运行环境: + +- **NeoForge 26.x / MC 26.1+** ➔ 优先匹配 Java 25 (`MSLX://Java/25`) +- **MC 1.20.5 - 1.21.11** ➔ 推荐匹配 Java 21 (`MSLX://Java/21`) +- **MC 1.18 - 1.20.4** ➔ 推荐匹配 Java 17 (`MSLX://Java/17`) +- **MC 1.17 / 1.17.1** ➔ 推荐匹配 Java 16 (`MSLX://Java/16`) +- **MC 1.13 - 1.16.5** ➔ 推荐匹配 Java 11 (`MSLX://Java/11`) 或 Java 8 (`MSLX://Java/8`) +- **MC 1.12.2 及更低旧版本** ➔ 必须匹配 Java 8 (`MSLX://Java/8`) + +当用户要求“把 Java 切换为合适的版本”或修改设置时,绝对禁止反问用户或询问“你想用 Java 21 还是 25”,必须根据上述规则立即调用 `update_instance_settings` 工具执行切换! diff --git a/MSLX.Daemon/Skills/ServerOps.md b/MSLX.Daemon/Skills/ServerOps.md new file mode 100644 index 00000000..5c387842 --- /dev/null +++ b/MSLX.Daemon/Skills/ServerOps.md @@ -0,0 +1,30 @@ +# 运维工具与高级功能指南 + +你拥有全套智能化 MC 服务器运维与全模式部署工具库: + +1. **多模式开服部署 (`create_mc_server`)**: + 支持 MSLX 面板的全部 4 种开服模式: + - **快速在线部署**:传入 `server_type` (paper/neoforge/fabric 等) 和 `mc_version` (如 1.20.2),系统自动从镜像站获取核心并匹配推导最适合的 Java 大版本。 + - **整合包 / 压缩包部署**:传入 `package_local_path` (宿主机本地 `.zip` 绝对路径,如 `C:\modpack.zip`) 或 `package_url` (远程 zip URL),一键解压并部署整合包开服。 + - **MCDReforged (MCDR) 架构模式**:传入 `mcdr: true`,支持自定义 `mcdr_python` (Python可执行文件路径)、`mcdr_handler` (如 vanilla, paper, fabric, custom)、`mcdr_install` (自动 pip 安装) 及 `mcdr_pip_mirror` 镜像源。 + - **自定义 / 导入模式**:支持传入自定义宿主机存放路径 `path`、直接下载的核心 `core_url` 与核心文件名 `core_filename`,以及自定义内存 `max_m`/`min_m` 和 JVM 启动参数 `args`。 + +2. **日志排查与崩溃诊断 (`read_server_log`)**: + - 当用户询问“服务器为什么崩溃了”、“帮我看下报错”或发生崩服时,【优先调用 `read_server_log`】! + - 支持读取 `logs/latest.log` 末尾日志以及 `crash-reports/` 目录下最新的崩溃报告文件,采用 `FileShare.ReadWrite` 模式无锁安全读取。 + +3. **文件管理与高级文件操作 (`copy_server_file`, `move_server_file`, `delete_server_file`)**: + - **复制文件/目录 (`copy_server_file`)**:用于备份配置文件(如 `server.properties.bak`)、备份地图存档或复制 Mod/插件。 + - **移动/重命名 (`move_server_file`)**:用于移动备份文件、重命名存档目录,或者通过将崩服 Mod 从 `mod.jar` 重命名为 `mod.jar.disabled` 来禁用特定模组/插件! + +4. **Mod 与插件列举 (`list_server_mods_plugins`)**: + - 当用户询问“安装了哪些 Mod”、“查看插件列表”时,调用此工具获取包含 `.jar` 和 `.disabled` 模组/插件文件信息。 + +5. **系统性能与资源监测 (`query_system_metrics`)**: + - 当用户询问“服务器卡不卡”、“内存使用情况”时,调用此工具查询宿主机与服务器实例的真实内存、CPU 与配置限额。 + +6. **实例管理与控制 (`update_instance_settings`, `update_server_config`, `control_server`)**: + - 用于修改端口/正版验证、调整 Java 大版本以及控制服务器启动/停止/重启。 + +在每次文本回答的末尾,建议生成 3 个适合当前上下文的预设快捷回复选项: +<<>> diff --git a/MSLX.Daemon/Skills/SystemPrompt.md b/MSLX.Daemon/Skills/SystemPrompt.md new file mode 100644 index 00000000..1e365e05 --- /dev/null +++ b/MSLX.Daemon/Skills/SystemPrompt.md @@ -0,0 +1,7 @@ +# MSLX AI 助手核心定义 + +你是一个高效、果断的 Minecraft 服务器运维助手。 + +你的职责是协助用户管理 MSLX 系统的 Minecraft 服务器实例,包括启动/停止/重启服务器、查看与编辑配置文件、删除存档/插件/模组、管理 Java 运行环境以及部署新服务器。 + +当对话上下文中包含【当前用户界面上下文】且用户指令未显式指明服务器 ID 时,请默认直接作用于该当前服务器 ID。 diff --git a/MSLX.Daemon/Utils/AiSkillManager.cs b/MSLX.Daemon/Utils/AiSkillManager.cs new file mode 100644 index 00000000..8629b90a --- /dev/null +++ b/MSLX.Daemon/Utils/AiSkillManager.cs @@ -0,0 +1,91 @@ +using System.Reflection; +using System.Text; + +namespace MSLX.Daemon.Utils; + +public static class AiSkillManager +{ + private static readonly string[] SkillDiskPaths = new[] + { + Path.Combine(AppContext.BaseDirectory, "Skills"), + Path.Combine(Directory.GetCurrentDirectory(), "Skills"), + Path.Combine(Directory.GetCurrentDirectory(), "MSLX.Daemon", "Skills") + }; + + public static string GetSystemPrompt() + { + var sb = new StringBuilder(); + var loadedCount = 0; + + // 1. 优先从 Assembly 程序集内嵌资源中读取 (支持单文件 PublishSingleFile 打包) + try + { + var assembly = Assembly.GetExecutingAssembly(); + var resourceNames = assembly.GetManifestResourceNames() + .Where(r => r.EndsWith(".md", StringComparison.OrdinalIgnoreCase) && r.Contains("Skills", StringComparison.OrdinalIgnoreCase)) + .OrderBy(r => r) + .ToList(); + + foreach (var resourceName in resourceNames) + { + using var stream = assembly.GetManifestResourceStream(resourceName); + if (stream != null) + { + using var reader = new StreamReader(stream, Encoding.UTF8); + var content = reader.ReadToEnd().Trim(); + if (!string.IsNullOrWhiteSpace(content)) + { + sb.AppendLine(content); + sb.AppendLine(); + loadedCount++; + } + } + } + } + catch (Exception ex) + { + Console.WriteLine($"[AiSkillManager] 读取程序集内嵌 Skill 资源异常: {ex.Message}"); + } + + // 2. 如果内嵌资源未读取到,降级尝试从磁盘 Skills 目录读取 + if (loadedCount == 0) + { + foreach (var dirPath in SkillDiskPaths) + { + if (!Directory.Exists(dirPath)) continue; + + var skillFiles = Directory.GetFiles(dirPath, "*.md", SearchOption.TopDirectoryOnly); + Array.Sort(skillFiles); + + foreach (var file in skillFiles) + { + try + { + var content = File.ReadAllText(file, Encoding.UTF8).Trim(); + if (!string.IsNullOrWhiteSpace(content)) + { + sb.AppendLine(content); + sb.AppendLine(); + loadedCount++; + } + } + catch (Exception ex) + { + Console.WriteLine($"[AiSkillManager] 读取磁盘 Skill 文件 {file} 异常: {ex.Message}"); + } + } + + if (loadedCount > 0) break; + } + } + + if (loadedCount == 0 || sb.Length == 0) + { + return """ +你是一个高效、果断的 Minecraft 服务器运维助手。当用户提出需求时,优先调用工具函数(Function Calling)完成操作。 +"""; + } + + return sb.ToString().Trim(); + } +} diff --git a/MSLX.WebPanel/package.json b/MSLX.WebPanel/package.json index 80448be1..7790721a 100644 --- a/MSLX.WebPanel/package.json +++ b/MSLX.WebPanel/package.json @@ -31,6 +31,7 @@ "@eslint/js": "^9.38.0", "@microsoft/signalr": "^10.0.0", "@tailwindcss/vite": "^4.2.1", + "@tdesign-vue-next/chat": "^0.6.0", "@vueuse/core": "^14.2.1", "@xterm/addon-fit": "^0.11.0", "@xterm/xterm": "^6.0.0", diff --git a/MSLX.WebPanel/pnpm-lock.yaml b/MSLX.WebPanel/pnpm-lock.yaml index bf21842b..8a897875 100644 --- a/MSLX.WebPanel/pnpm-lock.yaml +++ b/MSLX.WebPanel/pnpm-lock.yaml @@ -50,6 +50,9 @@ importers: '@tailwindcss/vite': specifier: ^4.2.1 version: 4.2.1(vite@7.3.5(@types/node@25.0.10)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.32.0)(stylus@0.57.0)(yaml@2.8.2)) + '@tdesign-vue-next/chat': + specifier: ^0.6.0 + version: 0.6.0(omi@7.7.13)(vue@3.5.29(typescript@5.9.3)) '@vueuse/core': specifier: ^14.2.1 version: 14.2.1(vue@3.5.29(typescript@5.9.3)) @@ -228,6 +231,9 @@ importers: packages: + '@antfu/install-pkg@1.1.0': + resolution: {integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==} + '@babel/code-frame@7.28.6': resolution: {integrity: sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==} engines: {node: '>=6.9.0'} @@ -362,12 +368,18 @@ packages: resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} engines: {node: '>=6.9.0'} + '@braintree/sanitize-url@7.1.2': + resolution: {integrity: sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA==} + '@cacheable/memory@2.0.7': resolution: {integrity: sha512-RbxnxAMf89Tp1dLhXMS7ceft/PGsDl1Ip7T20z5nZ+pwIAsQ1p2izPjVG69oCLv/jfQ7HDPHTWK0c9rcAWXN3A==} '@cacheable/utils@2.3.3': resolution: {integrity: sha512-JsXDL70gQ+1Vc2W/KUFfkAJzgb4puKwwKehNLuB+HrNKWf91O736kGfxn4KujXCCSuh6mRRL4XEB0PkAFjWS0A==} + '@chevrotain/types@11.1.2': + resolution: {integrity: sha512-U+HFai5+zmJCkK86QsaJtoITlboZHBqrVketcO2ROv865xfCMSFpELQoz1GkX5GzME8pTa+3kbKrZHQtI0gdbw==} + '@codemirror/autocomplete@6.20.0': resolution: {integrity: sha512-bOwvTOIJcG5FVo5gUUupiwYh8MioPLQ4UcqbcRf7UQ98X90tCa9E1kZ3Z7tqwpZxYyOvh1YTYbmZE9RTfTp5hg==} @@ -452,6 +464,9 @@ packages: '@codemirror/language@6.12.1': resolution: {integrity: sha512-Fa6xkSiuGKc8XC8Cn96T+TQHYj4ZZ7RdFmXA3i9xe/3hLHfwPZdM+dqfX0Cp0zQklBKhVD8Yzc8LS45rkqcwpQ==} + '@codemirror/language@6.12.4': + resolution: {integrity: sha512-1q4PaT+o6PbgpkJt4Q8Fv5XJxTy4FUZ4MWETtyiDw3J0Pyr9E2vqcKL+k9wcvjNTIsauxvE7OfmWj3FRPHQ76A==} + '@codemirror/legacy-modes@6.5.2': resolution: {integrity: sha512-/jJbwSTazlQEDOQw2FJ8LEEKVS72pU0lx6oM54kGpL8t/NJ2Jda3CZ4pcltiKTdqYSRk3ug1B3pil1gsjA6+8Q==} @@ -802,6 +817,12 @@ packages: resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} + '@iconify/types@2.0.0': + resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} + + '@iconify/utils@3.1.4': + resolution: {integrity: sha512-b1S7B1k9ohZ+iNTi2ATxbRYG9fTrJmUT0rc46bvVnNxqNRGW7dyo/vRREwyniI5IRN2RSJHDcm+s3BjWrSAjHw==} + '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} @@ -884,6 +905,9 @@ packages: '@material/material-color-utilities@0.1.2': resolution: {integrity: sha512-uKkkx7Xo1AZJR0eMjH902rAb0vEjDbb3tgXt0cC3SHbpDGkPNhkuDKtxnw147axOM6uKuI7vywHRQlDrjDGhCA==} + '@mermaid-js/parser@1.2.0': + resolution: {integrity: sha512-oYPyv8A4As1yH5Bx+04iQEQxXuIQDe0GKCNSRgao6z8AM9jixXIfP0vsppRLvGf+nKIOb9/LdpWA4YuJiVvESA==} + '@microsoft/signalr@10.0.0': resolution: {integrity: sha512-0BRqz/uCx3JdrOqiqgFhih/+hfTERaUfCZXFB52uMaZJrKaPRzHzMuqVsJC/V3pt7NozcNXGspjKiQEK+X7P2w==} @@ -906,6 +930,18 @@ packages: '@popperjs/core@2.11.8': resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} + '@replit/codemirror-vim-core@0.1.0': + resolution: {integrity: sha512-1i6EBKpcNfDKvTmTh6N6g9lL6udD5t+uFNh4JCqozRnVlvUGOps7h/QzS2ne4zcvPUjvApKpmcP7Grc3fNbZiQ==} + + '@replit/codemirror-vim@6.4.0': + resolution: {integrity: sha512-t9UMDNhkmeAkl0uRbiJVotv97bGD6mf4GyJJoEbrjUqa/Pov0s9eL+4AaI0Xto3OGri17i9qwIpT0JbVlVXt8A==} + peerDependencies: + '@codemirror/commands': 6.x.x + '@codemirror/language': 6.x.x + '@codemirror/search': 6.x.x + '@codemirror/state': 6.x.x + '@codemirror/view': 6.x.x + '@rolldown/pluginutils@1.0.0-beta.53': resolution: {integrity: sha512-vENRlFU4YbrwVqNDZ7fLvy+JR1CRkyr01jhSiDpE1u6py3OMzQfztQU2jxykW3ALNxO4kSlqIDeYyD0Y9RcQeQ==} @@ -1147,6 +1183,15 @@ packages: peerDependencies: vite: ^5.2.0 || ^6 || ^7 + '@tdesign-vue-next/chat@0.6.0': + resolution: {integrity: sha512-XCCc3Qyji0vvgOhwjNmm/TteydDXfdvtSihoTX+chJsoiepB4d9c6M2hL2DZAcAc7vVxcG4uWRu2icdLpyZGGA==} + peerDependencies: + vue: '>=3.1.0' + + '@tootallnate/once@2.0.1': + resolution: {integrity: sha512-HqmEUIGRJ5fSXchkVgR5F7qn48bDBzv0kWj/Kfu5e6uci4UlEeng4331LnBkWffb++Ei3FOVLxo8JJWMFBDMeQ==} + engines: {node: '>= 10'} + '@trysound/sax@0.2.0': resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==} engines: {node: '>=10.13.0'} @@ -1154,9 +1199,105 @@ packages: '@types/conventional-commits-parser@5.0.2': resolution: {integrity: sha512-BgT2szDXnVypgpNxOK8aL5SGjUdaQbC++WZNjF1Qge3Og2+zhHj+RWhmehLhYyvQwqAmvezruVfOf8+3m74W+g==} + '@types/d3-array@3.2.2': + resolution: {integrity: sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==} + + '@types/d3-axis@3.0.6': + resolution: {integrity: sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==} + + '@types/d3-brush@3.0.6': + resolution: {integrity: sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==} + + '@types/d3-chord@3.0.6': + resolution: {integrity: sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==} + + '@types/d3-color@3.1.3': + resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==} + + '@types/d3-contour@3.0.6': + resolution: {integrity: sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==} + + '@types/d3-delaunay@6.0.4': + resolution: {integrity: sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==} + + '@types/d3-dispatch@3.0.7': + resolution: {integrity: sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==} + + '@types/d3-drag@3.0.7': + resolution: {integrity: sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==} + + '@types/d3-dsv@3.0.7': + resolution: {integrity: sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==} + + '@types/d3-ease@3.0.2': + resolution: {integrity: sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==} + + '@types/d3-fetch@3.0.7': + resolution: {integrity: sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==} + + '@types/d3-force@3.0.10': + resolution: {integrity: sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==} + + '@types/d3-format@3.0.4': + resolution: {integrity: sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==} + + '@types/d3-geo@3.1.1': + resolution: {integrity: sha512-65Emv9fQiQQqphLlRkuQ5ypPsOmWPhtBGCMv61JDPEPMvsx+gzhGf74yw1a78xFKPj6zw4AgQICJoQv0vK9M2w==} + + '@types/d3-hierarchy@3.1.7': + resolution: {integrity: sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==} + + '@types/d3-interpolate@3.0.4': + resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==} + + '@types/d3-path@3.1.1': + resolution: {integrity: sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==} + + '@types/d3-polygon@3.0.2': + resolution: {integrity: sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==} + + '@types/d3-quadtree@3.0.6': + resolution: {integrity: sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==} + + '@types/d3-random@3.0.4': + resolution: {integrity: sha512-UHYId5WTCx4L4YNel7NU00XUXXgvgpgZOvp10PuvsQENjMDXhh2RyFc0KBjO7B45ne4Ha1yVH7ii0vnzKkuzWA==} + + '@types/d3-scale-chromatic@3.1.0': + resolution: {integrity: sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==} + + '@types/d3-scale@4.0.9': + resolution: {integrity: sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==} + + '@types/d3-selection@3.0.11': + resolution: {integrity: sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==} + + '@types/d3-shape@3.1.8': + resolution: {integrity: sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==} + + '@types/d3-time-format@4.0.3': + resolution: {integrity: sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==} + + '@types/d3-time@3.0.4': + resolution: {integrity: sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==} + + '@types/d3-timer@3.0.2': + resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==} + + '@types/d3-transition@3.0.9': + resolution: {integrity: sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==} + + '@types/d3-zoom@3.0.8': + resolution: {integrity: sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==} + + '@types/d3@7.4.3': + resolution: {integrity: sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==} + '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + '@types/geojson@7946.0.16': + resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==} + '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} @@ -1190,6 +1331,9 @@ packages: '@types/tinycolor2@1.4.6': resolution: {integrity: sha512-iEN8J0BoMnsWBqjVbWH/c0G0Hh7O21lpR2/+PrvAVgWdzL7eexIFm4JN/Wn10PTcmNdtS6U67r499mlWMXOxNw==} + '@types/trusted-types@2.0.7': + resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} + '@types/validator@13.15.10': resolution: {integrity: sha512-T8L6i7wCuyoK8A/ZeLYt1+q0ty3Zb9+qbSSvrIVitzT3YjZqkTZ40IbRsPanlB4h1QB3JVL1SYCdR6ngtFYcuA==} @@ -1258,6 +1402,9 @@ packages: resolution: {integrity: sha512-oy+wV7xDKFPRyNggmXuZQSBzvoLnpmJs+GhzRhPjrxl2b/jIlyjVokzm47CZCDUdXKr2zd7ZLodPfOBpOPyPlg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@upsetjs/venn.js@2.0.0': + resolution: {integrity: sha512-WbBhLrooyePuQ1VZxrJjtLvTc4NVfpOyKx0sKqioq9bX1C1m7Jgykkn8gLrtwumBioXIqam8DLxp88Adbue6Hw==} + '@vavt/copy2clipboard@1.0.3': resolution: {integrity: sha512-HtG48r2FBYp9eRvGB3QGmtRBH1zzRRAVvFbGgFstOwz4/DDaNiX0uZc3YVKPydqgOav26pibr9MtoCaWxn7aeA==} @@ -1395,20 +1542,40 @@ packages: resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==} hasBin: true + abab@2.0.6: + resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==} + deprecated: Use your platform's native atob() and btoa() methods instead + abort-controller@3.0.0: resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} engines: {node: '>=6.5'} + acorn-globals@6.0.0: + resolution: {integrity: sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==} + acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + acorn-walk@7.2.0: + resolution: {integrity: sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==} + engines: {node: '>=0.4.0'} + + acorn@7.4.1: + resolution: {integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==} + engines: {node: '>=0.4.0'} + hasBin: true + acorn@8.15.0: resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} engines: {node: '>=0.4.0'} hasBin: true + agent-base@6.0.2: + resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} + engines: {node: '>= 6.0.0'} + ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} @@ -1543,6 +1710,9 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} + browser-process-hrtime@1.0.0: + resolution: {integrity: sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==} + browserslist@4.28.1: resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -1592,9 +1762,16 @@ packages: chardet@0.7.0: resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} + cherry-markdown@0.11.9: + resolution: {integrity: sha512-8z1N1IBFKfpeGSu2Ht2s75I6rpmQQ6eSytHHy9ED+mbmIq0lj2YNSVeUkIDTHD9kY2rLlas4adYuPkIOndXB+w==} + engines: {node: '>=22'} + chroma-js@2.1.2: resolution: {integrity: sha512-ri/ouYDWuxfus3UcaMxC1Tfp3IE9K5iQzxc2hSxbBRVNQFut1UuGAsZmiAf2mOUubzGJwgMSv9lHg+XqLaz1QQ==} + class-variance-authority@0.7.1: + resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} + cli-cursor@3.1.0: resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} engines: {node: '>=8'} @@ -1626,6 +1803,10 @@ packages: resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} engines: {node: '>=0.8'} + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + codemirror@6.0.2: resolution: {integrity: sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw==} @@ -1663,6 +1844,10 @@ packages: resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} engines: {node: '>= 10'} + commander@8.3.0: + resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} + engines: {node: '>= 12'} + commitizen@4.3.1: resolution: {integrity: sha512-gwAPAVTy/j5YcOOebcCRIijn+mSjWJC+IYKivTu6aG8Ei/scoXgfsMRnuAk6b0GRste2J4NGxVdMN3ZpfNaVaw==} engines: {node: '>= 12'} @@ -1674,6 +1859,10 @@ packages: concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + construct-style-sheets-polyfill@3.0.1: + resolution: {integrity: sha512-vyAiTcS3i29IxjzJ12K2MGUUBYgrF69p6fKD+8ZPRLFhwTtuaxXLpazl4Yw4FITsQXwUMrh8juI1bOHWYjzkwA==} + engines: {npm: '>=7'} + conventional-changelog-angular@7.0.0: resolution: {integrity: sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ==} engines: {node: '>=16'} @@ -1696,9 +1885,18 @@ packages: copy-anything@2.0.6: resolution: {integrity: sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==} + copy-to-clipboard@3.3.3: + resolution: {integrity: sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==} + core-js-pure@3.48.0: resolution: {integrity: sha512-1slJgk89tWC51HQ1AEqG+s2VuwpTRr8ocu4n20QUcH1v9lAN0RXen0Q0AABa/DK1I7RrNWLucplOHMx8hfTGTw==} + cose-base@1.0.3: + resolution: {integrity: sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==} + + cose-base@2.2.0: + resolution: {integrity: sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==} + cosmiconfig-typescript-loader@6.2.0: resolution: {integrity: sha512-GEN39v7TgdxgIoNcdkRE3uiAzQt3UXLyHbRHD6YoL048XAeOomyxaP+Hh/+2C6C2wYjxJ2onhJcsQp+L4YEkVQ==} engines: {node: '>=v18'} @@ -1770,17 +1968,187 @@ packages: resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} + cssom@0.3.8: + resolution: {integrity: sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==} + + cssom@0.5.0: + resolution: {integrity: sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==} + + cssstyle@2.3.0: + resolution: {integrity: sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==} + engines: {node: '>=8'} + csstype@3.2.3: resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + cytoscape-cose-bilkent@4.1.0: + resolution: {integrity: sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==} + peerDependencies: + cytoscape: ^3.2.0 + + cytoscape-fcose@2.2.0: + resolution: {integrity: sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==} + peerDependencies: + cytoscape: ^3.2.0 + + cytoscape@3.34.0: + resolution: {integrity: sha512-62rNSrioXw93uliKFBwjukeQyeWwH2PqDrTac31r2P6464u3AUvTk0xS4LVvT251g7IgkFunrI48ZEZGjywSOg==} + engines: {node: '>=0.10'} + cz-conventional-changelog@3.3.0: resolution: {integrity: sha512-U466fIzU5U22eES5lTNiNbZ+d8dfcHcssH4o7QsdWaCcRs/feIPCxKYSWkYBNs5mny7MvEfwpTLWjvbm94hecw==} engines: {node: '>= 10'} + d3-array@2.12.1: + resolution: {integrity: sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==} + + d3-array@3.2.4: + resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==} + engines: {node: '>=12'} + + d3-axis@3.0.0: + resolution: {integrity: sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==} + engines: {node: '>=12'} + + d3-brush@3.0.0: + resolution: {integrity: sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==} + engines: {node: '>=12'} + + d3-chord@3.0.1: + resolution: {integrity: sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==} + engines: {node: '>=12'} + + d3-color@3.1.0: + resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==} + engines: {node: '>=12'} + + d3-contour@4.0.2: + resolution: {integrity: sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==} + engines: {node: '>=12'} + + d3-delaunay@6.0.4: + resolution: {integrity: sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==} + engines: {node: '>=12'} + + d3-dispatch@3.0.1: + resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==} + engines: {node: '>=12'} + + d3-drag@3.0.0: + resolution: {integrity: sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==} + engines: {node: '>=12'} + + d3-dsv@3.0.1: + resolution: {integrity: sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==} + engines: {node: '>=12'} + hasBin: true + + d3-ease@3.0.1: + resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==} + engines: {node: '>=12'} + + d3-fetch@3.0.1: + resolution: {integrity: sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==} + engines: {node: '>=12'} + + d3-force@3.0.0: + resolution: {integrity: sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==} + engines: {node: '>=12'} + + d3-format@3.1.2: + resolution: {integrity: sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==} + engines: {node: '>=12'} + + d3-geo@3.1.1: + resolution: {integrity: sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==} + engines: {node: '>=12'} + + d3-hierarchy@3.1.2: + resolution: {integrity: sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==} + engines: {node: '>=12'} + + d3-interpolate@3.0.1: + resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==} + engines: {node: '>=12'} + + d3-path@1.0.9: + resolution: {integrity: sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==} + + d3-path@3.1.0: + resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==} + engines: {node: '>=12'} + + d3-polygon@3.0.1: + resolution: {integrity: sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==} + engines: {node: '>=12'} + + d3-quadtree@3.0.1: + resolution: {integrity: sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==} + engines: {node: '>=12'} + + d3-random@3.0.1: + resolution: {integrity: sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==} + engines: {node: '>=12'} + + d3-sankey@0.12.3: + resolution: {integrity: sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==} + + d3-scale-chromatic@3.1.0: + resolution: {integrity: sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==} + engines: {node: '>=12'} + + d3-scale@4.0.2: + resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==} + engines: {node: '>=12'} + + d3-selection@3.0.0: + resolution: {integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==} + engines: {node: '>=12'} + + d3-shape@1.3.7: + resolution: {integrity: sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==} + + d3-shape@3.2.0: + resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==} + engines: {node: '>=12'} + + d3-time-format@4.1.0: + resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==} + engines: {node: '>=12'} + + d3-time@3.1.0: + resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==} + engines: {node: '>=12'} + + d3-timer@3.0.1: + resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==} + engines: {node: '>=12'} + + d3-transition@3.0.1: + resolution: {integrity: sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==} + engines: {node: '>=12'} + peerDependencies: + d3-selection: 2 - 3 + + d3-zoom@3.0.0: + resolution: {integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==} + engines: {node: '>=12'} + + d3@7.9.0: + resolution: {integrity: sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==} + engines: {node: '>=12'} + + dagre-d3-es@7.0.14: + resolution: {integrity: sha512-P4rFMVq9ESWqmOgK+dlXvOtLwYg0i7u0HBGJER0LZDJT2VHIPAMZ/riPxqJceWMStH5+E61QxFra9kIS3AqdMg==} + dargs@8.1.0: resolution: {integrity: sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==} engines: {node: '>=12'} + data-urls@3.0.2: + resolution: {integrity: sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==} + engines: {node: '>=12'} + data-view-buffer@1.0.2: resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} engines: {node: '>= 0.4'} @@ -1796,6 +2164,9 @@ packages: dayjs@1.11.19: resolution: {integrity: sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==} + dayjs@1.11.21: + resolution: {integrity: sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA==} + de-indent@1.0.2: resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==} @@ -1816,6 +2187,9 @@ packages: supports-color: optional: true + decimal.js@10.6.0: + resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} + decode-uri-component@0.2.2: resolution: {integrity: sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==} engines: {node: '>=0.10'} @@ -1837,6 +2211,9 @@ packages: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} + delaunator@5.1.0: + resolution: {integrity: sha512-AGrQ4QSgssa1NGmWmLPqN5NY2KajF5MqxetNEO+o0n3ZwZZeTmt7bBnvzHWrmkZFxGgr4HdyFgelzgi06otLuQ==} + delayed-stream@1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} @@ -1870,10 +2247,18 @@ packages: domelementtype@2.3.0: resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + domexception@4.0.0: + resolution: {integrity: sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==} + engines: {node: '>=12'} + deprecated: Use your platform's native DOMException instead + domhandler@5.0.3: resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} engines: {node: '>= 4'} + dompurify@3.4.13: + resolution: {integrity: sha512-2vmYIoqjze2d+kakP8S/nS5shfsl587kzwEjcGlTdiksUVgFHnFCsLYDVj/JNqJVOQZGSYBTmuycv0PodwmnMQ==} + domutils@3.2.2: resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} @@ -1952,6 +2337,9 @@ packages: resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} engines: {node: '>= 0.4'} + es-toolkit@1.50.0: + resolution: {integrity: sha512-OyZKhUVvEep9ITEiwHn8GKnMRQIVqoSIX7WnRbkWgJkllCujilqP2rD0u979tkl8wqyc8ICwlc1UBVv/Sl1G6w==} + esbuild@0.27.2: resolution: {integrity: sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==} engines: {node: '>=18'} @@ -1969,6 +2357,11 @@ packages: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} + escodegen@2.1.0: + resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} + engines: {node: '>=6.0'} + hasBin: true + eslint-compat-utils@0.6.5: resolution: {integrity: sha512-vAUHYzue4YAa2hNACjB8HvUQj5yehAZgiClyFVVom9cP8z5NSFq3PwB/TtJslN2zAMgRX6FCFCjYBbQh71g5RQ==} engines: {node: '>=12'} @@ -2076,6 +2469,11 @@ packages: resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + esquery@1.7.0: resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} engines: {node: '>=0.10'} @@ -2128,6 +2526,9 @@ packages: resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} engines: {node: '>=8.6.0'} + fast-json-patch@3.1.1: + resolution: {integrity: sha512-vf6IHUX2SBcA+5/+4883dsIjpBTqmfBjmYiWK1savxQmFk4JfBMLa7ynTYOs1Rolp/T1betJxHiGD3g1Mn8lUQ==} + fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} @@ -2333,6 +2734,9 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + hachure-fill@0.5.2: + resolution: {integrity: sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==} + has-bigints@1.1.0: resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} engines: {node: '>= 0.4'} @@ -2376,6 +2780,10 @@ packages: resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} hasBin: true + highlight.js@11.11.1: + resolution: {integrity: sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==} + engines: {node: '>=12.0.0'} + homedir-polyfill@1.0.3: resolution: {integrity: sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==} engines: {node: '>=0.10.0'} @@ -2383,10 +2791,28 @@ packages: hookified@1.15.0: resolution: {integrity: sha512-51w+ZZGt7Zw5q7rM3nC4t3aLn/xvKDETsXqMczndvwyVQhAHfUmUuFBRFcos8Iyebtk7OAE9dL26wFNzZVVOkw==} + htm@3.1.1: + resolution: {integrity: sha512-983Vyg8NwUE7JkZ6NmOqpCZ+sh1bKv2iYTlUkzlWmA5JD2acKoxd4KVxbMmxX/85mtfdnDmTFoNKcg5DGAvxNQ==} + + html-encoding-sniffer@3.0.0: + resolution: {integrity: sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==} + engines: {node: '>=12'} + html-tags@3.3.1: resolution: {integrity: sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==} engines: {node: '>=8'} + htmlparser2@10.1.0: + resolution: {integrity: sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==} + + http-proxy-agent@5.0.0: + resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==} + engines: {node: '>= 6'} + + https-proxy-agent@5.0.1: + resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} + engines: {node: '>= 6'} + human-signals@5.0.0: resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} engines: {node: '>=16.17.0'} @@ -2420,6 +2846,9 @@ packages: engines: {node: '>=0.10.0'} hasBin: true + immer@10.2.0: + resolution: {integrity: sha512-d/+XTN3zfODyjr89gM3mPq1WNX2B8pYsu7eORitdwyA2sBubnTl3laYlBk4sXY5FUa5qTZGBDPJICVbvqzjlbw==} + import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} @@ -2453,6 +2882,13 @@ packages: resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} engines: {node: '>= 0.4'} + internmap@1.0.1: + resolution: {integrity: sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==} + + internmap@2.0.3: + resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} + engines: {node: '>=12'} + is-array-buffer@3.0.5: resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} engines: {node: '>= 0.4'} @@ -2544,6 +2980,9 @@ packages: resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} engines: {node: '>=0.10.0'} + is-potential-custom-element-name@1.0.1: + resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + is-regex@1.2.1: resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} engines: {node: '>= 0.4'} @@ -2619,6 +3058,15 @@ packages: resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} hasBin: true + jsdom@19.0.0: + resolution: {integrity: sha512-RYAyjCbxy/vri/CfnjUWJQQtZ3LKlLnDqj+9XLNnJPgEGeirZs3hllKR20re8LUZ6o1b1X4Jat+Qd26zmP41+A==} + engines: {node: '>=12'} + peerDependencies: + canvas: ^2.5.0 + peerDependenciesMeta: + canvas: + optional: true + jsesc@3.1.0: resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} engines: {node: '>=6'} @@ -2655,12 +3103,19 @@ packages: resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} engines: {'0': node >= 0.2.0} + katex@0.16.47: + resolution: {integrity: sha512-Eeo8Ys1doU1z+x8AZsPpQu+p/QcZBI5PeOo7QGQdy2x2m0MU/hYagBbGOmXwr5KVbEfVuWv9LpnQWeehogurjg==} + hasBin: true + keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} keyv@5.6.0: resolution: {integrity: sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==} + khroma@2.1.0: + resolution: {integrity: sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==} + kind-of@6.0.3: resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} engines: {node: '>=0.10.0'} @@ -2668,6 +3123,12 @@ packages: known-css-properties@0.37.0: resolution: {integrity: sha512-JCDrsP4Z1Sb9JwG0aJ8Eo2r7k4Ou5MwmThS/6lcIe1ICyb7UBJKGRIUUdqc2ASdE/42lgz6zFUnzAIhtXnBVrQ==} + layout-base@1.0.2: + resolution: {integrity: sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==} + + layout-base@2.0.1: + resolution: {integrity: sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==} + less@4.5.1: resolution: {integrity: sha512-UKgI3/KON4u6ngSsnDADsUERqhZknsVZbnuzlRZXLQCmfC/MDld42fTydUE9B+Mla1AL6SJ/Pp6SlEFi/AVGfw==} engines: {node: '>=14'} @@ -2855,6 +3316,9 @@ packages: lodash-es@4.17.23: resolution: {integrity: sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==} + lodash-es@4.18.1: + resolution: {integrity: sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==} + lodash.camelcase@4.3.0: resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} @@ -2933,6 +3397,9 @@ packages: resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==} engines: {node: '>=6'} + markdown-it-fence@0.1.3: + resolution: {integrity: sha512-mdZbkwJUyZXhyDX4QzD+WnIhxWBq9oIIJU22E6jCT7MHgIp79oEOJfwXIl/HqmfIxnXEdC47sBGn4h6chM4DKw==} + markdown-it-image-figures@2.1.1: resolution: {integrity: sha512-mwXSQ2nPeVUzCMIE3HlLvjRioopiqyJLNph0pyx38yf9mpqFDhNGnMpAXF9/A2Xv0oiF2cVyg9xwfF0HNAz05g==} engines: {node: '>=12.0.0'} @@ -2949,6 +3416,21 @@ packages: resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==} hasBin: true + marked-highlight@2.2.4: + resolution: {integrity: sha512-PZxisNMJDduSjc0q6uvjsnqqHCXc9s0eyzxDO9sB1eNGJnd/H1/Fu+z6g/liC1dfJdFW4SftMwMlLvsBhUPrqQ==} + peerDependencies: + marked: '>=4 <19' + + marked@12.0.2: + resolution: {integrity: sha512-qXUm7e/YKFoqFPYPa3Ukg9xlI5cyAtGmyEIzMfW//m6kXwCy2Ps9DYf5ioijFKQ8qyuscrHoY04iJGctu2Kg0Q==} + engines: {node: '>= 18'} + hasBin: true + + marked@16.4.2: + resolution: {integrity: sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==} + engines: {node: '>= 20'} + hasBin: true + math-intrinsics@1.1.0: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} @@ -2994,6 +3476,9 @@ packages: merge@2.1.1: resolution: {integrity: sha512-jz+Cfrg9GWOZbQAnDQ4hlVnQky+341Yk5ru8bZSe6sIDTCIg8n9i/u7hSQGSVOF3C7lH6mGtqjkiT9G4wFLL0w==} + mermaid@11.16.1: + resolution: {integrity: sha512-TQsq6u22fAn3rek5VOubrhKPo1g5hwC3FXUN9hiyupTckcYiGuuKGkNQrKYwGJkXUxZdojwRG46gsSCFZMDp4g==} + micromatch@4.0.8: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} @@ -3053,6 +3538,10 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + nanostores@0.11.4: + resolution: {integrity: sha512-k1oiVNN4hDK8NcNERSZLQiMfRzEGtfnvZvdBvey3SQbgn8Dcrk0h1I6vpxApjb10PFUflZrgJ2WEZyJQ+5v7YQ==} + engines: {node: ^18.0.0 || >=20.0.0} + natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} @@ -3087,6 +3576,9 @@ packages: nth-check@2.1.1: resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + nwsapi@2.2.24: + resolution: {integrity: sha512-7YRhZ3jS45LwmSCT4b2sVFHt/WuovaktDU07QrtOBY2PXskss5a9jfmR9jptyumwXST+rFjrmppMY1KT/yn35A==} + object-inspect@1.13.4: resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} engines: {node: '>= 0.4'} @@ -3111,6 +3603,18 @@ packages: resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} engines: {node: '>= 0.4'} + omi-transition@0.1.11: + resolution: {integrity: sha512-AsiLCOzMUuFRQ2juGXnSVjcOA2eOb4WdZzbiALbeyXCUgxdRTlE0ekO08IgyvAe9Rdc9fhx0iEgLnwd7ufneCg==} + + omi-vueify@0.0.12: + resolution: {integrity: sha512-Z7fircOM/vKmh4hKZss4V1h4u5FQAfvauzBwWTiTlHGJWsaXglGRiHatiSCWsKv8v9uNK/xYXz3p+2QbkevPZg==} + peerDependencies: + omi: '>=7.7.8' + vue: '>=3' + + omi@7.7.13: + resolution: {integrity: sha512-LA/rLhGE7PO/OVN9Mp4TG4YL3Dt5dERM9fsVyiPmE75CRCuyW7vVfrYK8Ee7EPbQuTycYRsfbrD1YBQytOY/jw==} + once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} @@ -3158,6 +3662,9 @@ packages: resolution: {integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + package-manager-detector@1.8.0: + resolution: {integrity: sha512-yQA4H19AmPEoMUeavPMDIe1higySl/gH/yaQrkT/s07Qp+7pp2hYz30N3z2l5BkjVkF9Ow6o0wjJamm2y7Sn0A==} + parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -3174,9 +3681,15 @@ packages: resolution: {integrity: sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==} engines: {node: '>=0.10.0'} + parse5@6.0.1: + resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==} + path-browserify@1.0.1: resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} + path-data-parser@0.1.0: + resolution: {integrity: sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==} + path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -3242,6 +3755,12 @@ packages: typescript: optional: true + points-on-curve@0.2.0: + resolution: {integrity: sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==} + + points-on-path@0.2.1: + resolution: {integrity: sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==} + possible-typed-array-names@1.1.0: resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} engines: {node: '>= 0.4'} @@ -3343,6 +3862,9 @@ packages: queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + reactive-signal@2.0.1: + resolution: {integrity: sha512-c+avHi2L+sHn9kkEsDZskRGtkfj6PtoTn1wGgutY2NZR9jCI0InXY7zqWbUXz6cGcuIXKMkQq/mtUiUXBFmKVA==} + readable-stream@3.6.2: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} @@ -3401,11 +3923,17 @@ packages: rfdc@1.4.1: resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + robust-predicates@3.0.3: + resolution: {integrity: sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA==} + rollup@4.56.0: resolution: {integrity: sha512-9FwVqlgUHzbXtDg9RCMgodF3Ua4Na6Gau+Sdt9vyCN4RhHfVKX2DCHy3BjMLTDd47ITDhYAnTwGulWTblJSDLg==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + roughjs@4.6.6: + resolution: {integrity: sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==} + run-async@2.4.1: resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==} engines: {node: '>=0.12.0'} @@ -3413,6 +3941,9 @@ packages: run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + rw@1.3.3: + resolution: {integrity: sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==} + rxjs@7.8.2: resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} @@ -3441,6 +3972,10 @@ packages: resolution: {integrity: sha512-1n3r/tGXO6b6VXMdFT54SHzT9ytu9yr7TaELowdYpMqY/Ao7EnlQGmAQ1+RatX7Tkkdm6hONI2owqNx2aZj5Sw==} engines: {node: '>=11.0.0'} + saxes@5.0.1: + resolution: {integrity: sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==} + engines: {node: '>=10'} + select@1.1.2: resolution: {integrity: sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA==} @@ -3664,6 +4199,9 @@ packages: engines: {node: '>=18.12.0'} hasBin: true + stylis@4.4.0: + resolution: {integrity: sha512-5Z9ZpRzfuH6l/UAvCPAPUo3665Nk2wLaZU3x+TLHKVzIz33+sbJqbtrYoC3KD4/uVOr2Zp+L0LySezP9OHV9yA==} + stylus@0.57.0: resolution: {integrity: sha512-yOI6G8WYfr0q8v8rRvE91wbxFU+rJPo760Va4MF6K0I6BZjO4r+xSynkvyPBP9tV1CIEUeRsiidjIs2rzb1CnQ==} hasBin: true @@ -3692,6 +4230,9 @@ packages: engines: {node: '>=14.0.0'} hasBin: true + symbol-tree@3.2.4: + resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + synckit@0.11.12: resolution: {integrity: sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==} engines: {node: ^14.18.0 || >=16.0.0} @@ -3700,6 +4241,9 @@ packages: resolution: {integrity: sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==} engines: {node: '>=10.0.0'} + tailwind-merge@2.6.1: + resolution: {integrity: sha512-Oo6tHdpZsGpkKG88HJ8RR1rg/RdnEkQEfMoEk2x1XRI3F1AxeU+ijRXpiVUF4UbLfcxxRGw6TbUINKYdWVsQTQ==} + tailwindcss@4.2.1: resolution: {integrity: sha512-/tBrSQ36vCleJkAOsy9kbNTgaxvGbyOamC30PRePTQe/o1MFwEKHQk4Cn7BNGaPtjp+PuUrByJehM1hgxfq4sw==} @@ -3712,12 +4256,31 @@ packages: peerDependencies: vue: ^3.0.0 + tdesign-icons-vue-next@0.4.7: + resolution: {integrity: sha512-0TRVH0L9O7vAMQ7j2vU/VYBMzS5FI+OVf7bWjwAVMmWXArsAyJI8mzpNWThZok96B6hsRY+2hK/NSOFZy8hMqA==} + peerDependencies: + vue: ^3.0.0 + + tdesign-icons-web-components@0.3.7: + resolution: {integrity: sha512-y0+tN6A9wgtbx1+j1APyKwrUcsJfzbzEzR/Sy+ioJuyUA9xfTPnvq6eEPMUSrzEKScTDHojP+vgwLUtszrdqmg==} + peerDependencies: + omi: '>=7.6.17' + tdesign-vue-next@1.18.2: resolution: {integrity: sha512-Y7hpEHNQPft7TP0TfgZoBMcXUkILOxZRtV0es7ZFcGmsFqkOl4W2p2amYx78Id4W40VizJXzxP1HzFXRI0MYqw==} engines: {node: '>= 18'} peerDependencies: vue: '>=3.1.0' + tdesign-vue-next@1.20.5: + resolution: {integrity: sha512-FVCmDBoXOKUDE77ZvRzpaSSs10PXbb4Tikma4wMCJHRqF9NilgkbLh4ympeOKt57wxOtOXsQfs7zU+AcCpsZXw==} + engines: {node: '>= 18'} + peerDependencies: + vue: '>=3.1.0' + + tdesign-web-components@1.3.1-alpha.13: + resolution: {integrity: sha512-x/ABoWigk7xtG6p7MzqGkkCLcpcPEUWtCRLjXdLCFwLHpldq3RqfxtVH+QmhpcCF1X1uZDXTMWQcTN7xPXDO9g==} + text-extensions@2.4.0: resolution: {integrity: sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==} engines: {node: '>=8'} @@ -3751,6 +4314,9 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} + toggle-selection@1.0.6: + resolution: {integrity: sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==} + tough-cookie@4.1.4: resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==} engines: {node: '>=6'} @@ -3758,12 +4324,20 @@ packages: tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + tr46@3.0.0: + resolution: {integrity: sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==} + engines: {node: '>=12'} + ts-api-utils@2.4.0: resolution: {integrity: sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==} engines: {node: '>=18.12'} peerDependencies: typescript: '>=4.8.4' + ts-dedent@2.3.0: + resolution: {integrity: sha512-JfJeIHke7y2egdGGgRAvpCwYFUsHlM2gPcrVOxFkznt/4uzQ7HFmvE63iFHVLBJNDuyDOQgijDK/tXH/f6Msjg==} + engines: {node: '>=6.10'} + tsconfig-paths@3.15.0: resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} @@ -3849,6 +4423,10 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + uuid@14.0.1: + resolution: {integrity: sha512-6ZxzVpzDXDa3bJWaHilVayA+BH/1zmxCJoVgvmqJnid/gPoKHxUrS/aC/T6LGQtNHT+XHG9fXPJB4d+IrU30Ew==} + hasBin: true + validator@13.15.26: resolution: {integrity: sha512-spH26xU080ydGggxRyR1Yhcbgx+j3y5jbNXk/8L+iRvdIEQ4uTRH2Sgf2dokud6Q4oAtsbNvJ1Ft+9xmm6IZcA==} engines: {node: '>= 0.10'} @@ -3946,15 +4524,47 @@ packages: typescript: optional: true + w3c-hr-time@1.0.2: + resolution: {integrity: sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==} + deprecated: Use your platform's native performance.now() and performance.timeOrigin. + w3c-keyname@2.2.8: resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==} + w3c-xmlserializer@3.0.0: + resolution: {integrity: sha512-3WFqGEgSXIyGhOmAFtlicJNMjEps8b1MG31NCA0/vOF9+nKMUW1ckhi9cnNHmf88Rzw5V+dwIwsm2C7X8k9aQg==} + engines: {node: '>=12'} + wcwidth@1.0.1: resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} + weakmap-polyfill@2.0.4: + resolution: {integrity: sha512-ZzxBf288iALJseijWelmECm/1x7ZwQn3sMYIkDr2VvZp7r6SEKuT8D0O9Wiq6L9Nl5mazrOMcmiZE/2NCenaxw==} + engines: {node: '>=8.10.0'} + webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + webidl-conversions@7.0.0: + resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} + engines: {node: '>=12'} + + whatwg-encoding@2.0.0: + resolution: {integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==} + engines: {node: '>=12'} + + whatwg-mimetype@3.0.0: + resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==} + engines: {node: '>=12'} + + whatwg-url@10.0.0: + resolution: {integrity: sha512-CLxxCmdUby142H5FZzn4D8ikO1cmypvXVQktsgosNy4a4BHrDHeciBBGZhb0bNoR5/MltoCatso+vFjjGx8t0w==} + engines: {node: '>=12'} + + whatwg-url@11.0.0: + resolution: {integrity: sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==} + engines: {node: '>=12'} + whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} @@ -4014,12 +4624,27 @@ packages: utf-8-validate: optional: true - xml-name-validator@4.0.0: - resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==} - engines: {node: '>=12'} - - xss@1.0.15: - resolution: {integrity: sha512-FVdlVVC67WOIPvfOwhoMETV72f6GbW7aOabBC3WxN/oUdoEMDyLz4OgRv5/gck2ZeNqEQu+Tb0kloovXOfpYVg==} + ws@8.21.2: + resolution: {integrity: sha512-54dMVAo4WIe6SKy3vBgN+9bJZqqQ8IMRevAkOLQALhi49qkkQDQfWdAZ8KQlXiEabw88ARXXdUrlvtbKQX+aKw==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + xml-name-validator@4.0.0: + resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==} + engines: {node: '>=12'} + + xmlchars@2.2.0: + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + + xss@1.0.15: + resolution: {integrity: sha512-FVdlVVC67WOIPvfOwhoMETV72f6GbW7aOabBC3WxN/oUdoEMDyLz4OgRv5/gck2ZeNqEQu+Tb0kloovXOfpYVg==} engines: {node: '>= 0.10.0'} hasBin: true @@ -4051,11 +4676,20 @@ packages: resolution: {integrity: sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==} engines: {node: '>=12.20'} + zod@3.25.76: + resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + zrender@6.0.0: resolution: {integrity: sha512-41dFXEEXuJpNecuUQq6JlbybmnHaqqpGlbH1yxnA5V9MMP4SbohSVZsJIwz+zdjQXSSlR1Vc34EgH1zxyTDvhg==} snapshots: + '@antfu/install-pkg@1.1.0': + dependencies: + package-manager-detector: 1.8.0 + tinyexec: 1.0.2 + optional: true + '@babel/code-frame@7.28.6': dependencies: '@babel/helper-validator-identifier': 7.28.5 @@ -4239,6 +4873,9 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 + '@braintree/sanitize-url@7.1.2': + optional: true + '@cacheable/memory@2.0.7': dependencies: '@cacheable/utils': 2.3.3 @@ -4251,6 +4888,9 @@ snapshots: hashery: 1.4.0 keyv: 5.6.0 + '@chevrotain/types@11.1.2': + optional: true + '@codemirror/autocomplete@6.20.0': dependencies: '@codemirror/language': 6.12.1 @@ -4496,6 +5136,15 @@ snapshots: '@lezer/lr': 1.4.7 style-mod: 4.1.3 + '@codemirror/language@6.12.4': + dependencies: + '@codemirror/state': 6.6.0 + '@codemirror/view': 6.42.1 + '@lezer/common': 1.5.0 + '@lezer/highlight': 1.2.3 + '@lezer/lr': 1.4.7 + style-mod: 4.1.3 + '@codemirror/legacy-modes@6.5.2': dependencies: '@codemirror/language': 6.12.1 @@ -4842,6 +5491,16 @@ snapshots: '@humanwhocodes/retry@0.4.3': {} + '@iconify/types@2.0.0': + optional: true + + '@iconify/utils@3.1.4': + dependencies: + '@antfu/install-pkg': 1.1.0 + '@iconify/types': 2.0.0 + import-meta-resolve: 4.2.0 + optional: true + '@jridgewell/gen-mapping@0.3.13': dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -4966,6 +5625,11 @@ snapshots: '@material/material-color-utilities@0.1.2': {} + '@mermaid-js/parser@1.2.0': + dependencies: + '@chevrotain/types': 11.1.2 + optional: true + '@microsoft/signalr@10.0.0': dependencies: abort-controller: 3.0.0 @@ -4994,6 +5658,17 @@ snapshots: '@popperjs/core@2.11.8': {} + '@replit/codemirror-vim-core@0.1.0': {} + + '@replit/codemirror-vim@6.4.0(@codemirror/commands@6.10.3)(@codemirror/language@6.12.4)(@codemirror/search@6.6.0)(@codemirror/state@6.6.0)(@codemirror/view@6.42.1)': + dependencies: + '@codemirror/commands': 6.10.3 + '@codemirror/language': 6.12.4 + '@codemirror/search': 6.6.0 + '@codemirror/state': 6.6.0 + '@codemirror/view': 6.42.1 + '@replit/codemirror-vim-core': 0.1.0 + '@rolldown/pluginutils@1.0.0-beta.53': {} '@rolldown/pluginutils@1.0.0-rc.1': {} @@ -5143,14 +5818,188 @@ snapshots: tailwindcss: 4.2.1 vite: 7.3.5(@types/node@25.0.10)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.32.0)(stylus@0.57.0)(yaml@2.8.2) + '@tdesign-vue-next/chat@0.6.0(omi@7.7.13)(vue@3.5.29(typescript@5.9.3))': + dependencies: + '@babel/runtime': 7.28.6 + '@types/lodash-es': 4.17.12 + clipboard: 2.0.11 + highlight.js: 11.11.1 + lodash-es: 4.18.1 + marked: 12.0.2 + marked-highlight: 2.2.4(marked@12.0.2) + omi-vueify: 0.0.12(omi@7.7.13)(vue@3.5.29(typescript@5.9.3)) + tdesign-icons-vue-next: 0.4.7(vue@3.5.29(typescript@5.9.3)) + tdesign-vue-next: 1.20.5(vue@3.5.29(typescript@5.9.3)) + tdesign-web-components: 1.3.1-alpha.13 + vue: 3.5.29(typescript@5.9.3) + transitivePeerDependencies: + - bufferutil + - canvas + - omi + - supports-color + - utf-8-validate + + '@tootallnate/once@2.0.1': {} + '@trysound/sax@0.2.0': {} '@types/conventional-commits-parser@5.0.2': dependencies: '@types/node': 25.0.10 + '@types/d3-array@3.2.2': + optional: true + + '@types/d3-axis@3.0.6': + dependencies: + '@types/d3-selection': 3.0.11 + optional: true + + '@types/d3-brush@3.0.6': + dependencies: + '@types/d3-selection': 3.0.11 + optional: true + + '@types/d3-chord@3.0.6': + optional: true + + '@types/d3-color@3.1.3': + optional: true + + '@types/d3-contour@3.0.6': + dependencies: + '@types/d3-array': 3.2.2 + '@types/geojson': 7946.0.16 + optional: true + + '@types/d3-delaunay@6.0.4': + optional: true + + '@types/d3-dispatch@3.0.7': + optional: true + + '@types/d3-drag@3.0.7': + dependencies: + '@types/d3-selection': 3.0.11 + optional: true + + '@types/d3-dsv@3.0.7': + optional: true + + '@types/d3-ease@3.0.2': + optional: true + + '@types/d3-fetch@3.0.7': + dependencies: + '@types/d3-dsv': 3.0.7 + optional: true + + '@types/d3-force@3.0.10': + optional: true + + '@types/d3-format@3.0.4': + optional: true + + '@types/d3-geo@3.1.1': + dependencies: + '@types/geojson': 7946.0.16 + optional: true + + '@types/d3-hierarchy@3.1.7': + optional: true + + '@types/d3-interpolate@3.0.4': + dependencies: + '@types/d3-color': 3.1.3 + optional: true + + '@types/d3-path@3.1.1': + optional: true + + '@types/d3-polygon@3.0.2': + optional: true + + '@types/d3-quadtree@3.0.6': + optional: true + + '@types/d3-random@3.0.4': + optional: true + + '@types/d3-scale-chromatic@3.1.0': + optional: true + + '@types/d3-scale@4.0.9': + dependencies: + '@types/d3-time': 3.0.4 + optional: true + + '@types/d3-selection@3.0.11': + optional: true + + '@types/d3-shape@3.1.8': + dependencies: + '@types/d3-path': 3.1.1 + optional: true + + '@types/d3-time-format@4.0.3': + optional: true + + '@types/d3-time@3.0.4': + optional: true + + '@types/d3-timer@3.0.2': + optional: true + + '@types/d3-transition@3.0.9': + dependencies: + '@types/d3-selection': 3.0.11 + optional: true + + '@types/d3-zoom@3.0.8': + dependencies: + '@types/d3-interpolate': 3.0.4 + '@types/d3-selection': 3.0.11 + optional: true + + '@types/d3@7.4.3': + dependencies: + '@types/d3-array': 3.2.2 + '@types/d3-axis': 3.0.6 + '@types/d3-brush': 3.0.6 + '@types/d3-chord': 3.0.6 + '@types/d3-color': 3.1.3 + '@types/d3-contour': 3.0.6 + '@types/d3-delaunay': 6.0.4 + '@types/d3-dispatch': 3.0.7 + '@types/d3-drag': 3.0.7 + '@types/d3-dsv': 3.0.7 + '@types/d3-ease': 3.0.2 + '@types/d3-fetch': 3.0.7 + '@types/d3-force': 3.0.10 + '@types/d3-format': 3.0.4 + '@types/d3-geo': 3.1.1 + '@types/d3-hierarchy': 3.1.7 + '@types/d3-interpolate': 3.0.4 + '@types/d3-path': 3.1.1 + '@types/d3-polygon': 3.0.2 + '@types/d3-quadtree': 3.0.6 + '@types/d3-random': 3.0.4 + '@types/d3-scale': 4.0.9 + '@types/d3-scale-chromatic': 3.1.0 + '@types/d3-selection': 3.0.11 + '@types/d3-shape': 3.1.8 + '@types/d3-time': 3.0.4 + '@types/d3-time-format': 4.0.3 + '@types/d3-timer': 3.0.2 + '@types/d3-transition': 3.0.9 + '@types/d3-zoom': 3.0.8 + optional: true + '@types/estree@1.0.8': {} + '@types/geojson@7946.0.16': + optional: true + '@types/json-schema@7.0.15': {} '@types/json5@0.0.29': {} @@ -5180,6 +6029,9 @@ snapshots: '@types/tinycolor2@1.4.6': {} + '@types/trusted-types@2.0.7': + optional: true + '@types/validator@13.15.10': {} '@types/web-bluetooth@0.0.21': {} @@ -5279,6 +6131,12 @@ snapshots: '@typescript-eslint/types': 8.53.1 eslint-visitor-keys: 4.2.1 + '@upsetjs/venn.js@2.0.0': + optionalDependencies: + d3-selection: 3.0.0 + d3-transition: 3.0.1(d3-selection@3.0.0) + optional: true + '@vavt/copy2clipboard@1.0.3': {} '@vavt/util@2.1.1': {} @@ -5483,16 +6341,33 @@ snapshots: jsonparse: 1.3.1 through: 2.3.8 + abab@2.0.6: {} + abort-controller@3.0.0: dependencies: event-target-shim: 5.0.1 + acorn-globals@6.0.0: + dependencies: + acorn: 7.4.1 + acorn-walk: 7.2.0 + acorn-jsx@5.3.2(acorn@8.15.0): dependencies: acorn: 8.15.0 + acorn-walk@7.2.0: {} + + acorn@7.4.1: {} + acorn@8.15.0: {} + agent-base@6.0.2: + dependencies: + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + ajv@6.12.6: dependencies: fast-deep-equal: 3.1.3 @@ -5642,6 +6517,8 @@ snapshots: dependencies: fill-range: 7.1.1 + browser-process-hrtime@1.0.0: {} + browserslist@4.28.1: dependencies: baseline-browser-mapping: 2.9.17 @@ -5701,10 +6578,36 @@ snapshots: chardet@0.7.0: {} + cherry-markdown@0.11.9: + dependencies: + '@codemirror/autocomplete': 6.20.2 + '@codemirror/commands': 6.10.3 + '@codemirror/lang-markdown': 6.5.0 + '@codemirror/language': 6.12.4 + '@codemirror/search': 6.6.0 + '@codemirror/state': 6.6.0 + '@codemirror/view': 6.42.1 + '@lezer/highlight': 1.2.3 + '@replit/codemirror-vim': 6.4.0(@codemirror/commands@6.10.3)(@codemirror/language@6.12.4)(@codemirror/search@6.6.0)(@codemirror/state@6.6.0)(@codemirror/view@6.42.1) + dompurify: 3.4.13 + htmlparser2: 10.1.0 + jsdom: 19.0.0 + optionalDependencies: + mermaid: 11.16.1 + transitivePeerDependencies: + - bufferutil + - canvas + - supports-color + - utf-8-validate + chroma-js@2.1.2: dependencies: cross-env: 6.0.3 + class-variance-authority@0.7.1: + dependencies: + clsx: 2.1.1 + cli-cursor@3.1.0: dependencies: restore-cursor: 3.1.0 @@ -5736,6 +6639,8 @@ snapshots: clone@1.0.4: {} + clsx@2.1.1: {} + codemirror@6.0.2: dependencies: '@codemirror/autocomplete': 6.20.2 @@ -5772,6 +6677,9 @@ snapshots: commander@7.2.0: {} + commander@8.3.0: + optional: true + commitizen@4.3.1(@types/node@25.0.10)(typescript@5.9.3): dependencies: cachedir: 2.3.0 @@ -5799,6 +6707,8 @@ snapshots: concat-map@0.0.1: {} + construct-style-sheets-polyfill@3.0.1: {} + conventional-changelog-angular@7.0.0: dependencies: compare-func: 2.0.0 @@ -5822,8 +6732,22 @@ snapshots: dependencies: is-what: 3.14.1 + copy-to-clipboard@3.3.3: + dependencies: + toggle-selection: 1.0.6 + core-js-pure@3.48.0: {} + cose-base@1.0.3: + dependencies: + layout-base: 1.0.2 + optional: true + + cose-base@2.2.0: + dependencies: + layout-base: 2.0.1 + optional: true + cosmiconfig-typescript-loader@6.2.0(@types/node@25.0.10)(cosmiconfig@9.0.0(typescript@5.9.3))(typescript@5.9.3): dependencies: '@types/node': 25.0.10 @@ -5897,8 +6821,31 @@ snapshots: dependencies: css-tree: 2.2.1 + cssom@0.3.8: {} + + cssom@0.5.0: {} + + cssstyle@2.3.0: + dependencies: + cssom: 0.3.8 + csstype@3.2.3: {} + cytoscape-cose-bilkent@4.1.0(cytoscape@3.34.0): + dependencies: + cose-base: 1.0.3 + cytoscape: 3.34.0 + optional: true + + cytoscape-fcose@2.2.0(cytoscape@3.34.0): + dependencies: + cose-base: 2.2.0 + cytoscape: 3.34.0 + optional: true + + cytoscape@3.34.0: + optional: true + cz-conventional-changelog@3.3.0(@types/node@25.0.10)(typescript@5.9.3): dependencies: chalk: 2.4.2 @@ -5913,8 +6860,222 @@ snapshots: - '@types/node' - typescript + d3-array@2.12.1: + dependencies: + internmap: 1.0.1 + optional: true + + d3-array@3.2.4: + dependencies: + internmap: 2.0.3 + optional: true + + d3-axis@3.0.0: + optional: true + + d3-brush@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-drag: 3.0.0 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-transition: 3.0.1(d3-selection@3.0.0) + optional: true + + d3-chord@3.0.1: + dependencies: + d3-path: 3.1.0 + optional: true + + d3-color@3.1.0: + optional: true + + d3-contour@4.0.2: + dependencies: + d3-array: 3.2.4 + optional: true + + d3-delaunay@6.0.4: + dependencies: + delaunator: 5.1.0 + optional: true + + d3-dispatch@3.0.1: + optional: true + + d3-drag@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-selection: 3.0.0 + optional: true + + d3-dsv@3.0.1: + dependencies: + commander: 7.2.0 + iconv-lite: 0.6.3 + rw: 1.3.3 + optional: true + + d3-ease@3.0.1: + optional: true + + d3-fetch@3.0.1: + dependencies: + d3-dsv: 3.0.1 + optional: true + + d3-force@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-quadtree: 3.0.1 + d3-timer: 3.0.1 + optional: true + + d3-format@3.1.2: + optional: true + + d3-geo@3.1.1: + dependencies: + d3-array: 3.2.4 + optional: true + + d3-hierarchy@3.1.2: + optional: true + + d3-interpolate@3.0.1: + dependencies: + d3-color: 3.1.0 + optional: true + + d3-path@1.0.9: + optional: true + + d3-path@3.1.0: + optional: true + + d3-polygon@3.0.1: + optional: true + + d3-quadtree@3.0.1: + optional: true + + d3-random@3.0.1: + optional: true + + d3-sankey@0.12.3: + dependencies: + d3-array: 2.12.1 + d3-shape: 1.3.7 + optional: true + + d3-scale-chromatic@3.1.0: + dependencies: + d3-color: 3.1.0 + d3-interpolate: 3.0.1 + optional: true + + d3-scale@4.0.2: + dependencies: + d3-array: 3.2.4 + d3-format: 3.1.2 + d3-interpolate: 3.0.1 + d3-time: 3.1.0 + d3-time-format: 4.1.0 + optional: true + + d3-selection@3.0.0: + optional: true + + d3-shape@1.3.7: + dependencies: + d3-path: 1.0.9 + optional: true + + d3-shape@3.2.0: + dependencies: + d3-path: 3.1.0 + optional: true + + d3-time-format@4.1.0: + dependencies: + d3-time: 3.1.0 + optional: true + + d3-time@3.1.0: + dependencies: + d3-array: 3.2.4 + optional: true + + d3-timer@3.0.1: + optional: true + + d3-transition@3.0.1(d3-selection@3.0.0): + dependencies: + d3-color: 3.1.0 + d3-dispatch: 3.0.1 + d3-ease: 3.0.1 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-timer: 3.0.1 + optional: true + + d3-zoom@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-drag: 3.0.0 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-transition: 3.0.1(d3-selection@3.0.0) + optional: true + + d3@7.9.0: + dependencies: + d3-array: 3.2.4 + d3-axis: 3.0.0 + d3-brush: 3.0.0 + d3-chord: 3.0.1 + d3-color: 3.1.0 + d3-contour: 4.0.2 + d3-delaunay: 6.0.4 + d3-dispatch: 3.0.1 + d3-drag: 3.0.0 + d3-dsv: 3.0.1 + d3-ease: 3.0.1 + d3-fetch: 3.0.1 + d3-force: 3.0.0 + d3-format: 3.1.2 + d3-geo: 3.1.1 + d3-hierarchy: 3.1.2 + d3-interpolate: 3.0.1 + d3-path: 3.1.0 + d3-polygon: 3.0.1 + d3-quadtree: 3.0.1 + d3-random: 3.0.1 + d3-scale: 4.0.2 + d3-scale-chromatic: 3.1.0 + d3-selection: 3.0.0 + d3-shape: 3.2.0 + d3-time: 3.1.0 + d3-time-format: 4.1.0 + d3-timer: 3.0.1 + d3-transition: 3.0.1(d3-selection@3.0.0) + d3-zoom: 3.0.0 + optional: true + + dagre-d3-es@7.0.14: + dependencies: + d3: 7.9.0 + lodash-es: 4.18.1 + optional: true + dargs@8.1.0: {} + data-urls@3.0.2: + dependencies: + abab: 2.0.6 + whatwg-mimetype: 3.0.0 + whatwg-url: 11.0.0 + data-view-buffer@1.0.2: dependencies: call-bound: 1.0.4 @@ -5935,6 +7096,9 @@ snapshots: dayjs@1.11.19: {} + dayjs@1.11.21: + optional: true + de-indent@1.0.2: {} debug@3.2.7: @@ -5945,6 +7109,8 @@ snapshots: dependencies: ms: 2.1.3 + decimal.js@10.6.0: {} + decode-uri-component@0.2.2: {} dedent@0.7.0: {} @@ -5967,6 +7133,11 @@ snapshots: has-property-descriptors: 1.0.2 object-keys: 1.1.1 + delaunator@5.1.0: + dependencies: + robust-predicates: 3.0.3 + optional: true + delayed-stream@1.0.0: {} delegate@3.2.0: {} @@ -5993,10 +7164,18 @@ snapshots: domelementtype@2.3.0: {} + domexception@4.0.0: + dependencies: + webidl-conversions: 7.0.0 + domhandler@5.0.3: dependencies: domelementtype: 2.3.0 + dompurify@3.4.13: + optionalDependencies: + '@types/trusted-types': 2.0.7 + domutils@3.2.2: dependencies: dom-serializer: 2.0.0 @@ -6128,6 +7307,9 @@ snapshots: is-date-object: 1.1.0 is-symbol: 1.1.1 + es-toolkit@1.50.0: + optional: true + esbuild@0.27.2: optionalDependencies: '@esbuild/aix-ppc64': 0.27.2 @@ -6163,6 +7345,14 @@ snapshots: escape-string-regexp@4.0.0: {} + escodegen@2.1.0: + dependencies: + esprima: 4.0.1 + estraverse: 5.3.0 + esutils: 2.0.3 + optionalDependencies: + source-map: 0.6.1 + eslint-compat-utils@0.6.5(eslint@9.39.2(jiti@2.6.1)): dependencies: eslint: 9.39.2(jiti@2.6.1) @@ -6312,6 +7502,8 @@ snapshots: acorn-jsx: 5.3.2(acorn@8.15.0) eslint-visitor-keys: 4.2.1 + esprima@4.0.1: {} + esquery@1.7.0: dependencies: estraverse: 5.3.0 @@ -6366,6 +7558,8 @@ snapshots: merge2: 1.4.1 micromatch: 4.0.8 + fast-json-patch@3.1.1: {} + fast-json-stable-stringify@2.1.0: {} fast-levenshtein@2.0.6: {} @@ -6595,6 +7789,9 @@ snapshots: graceful-fs@4.2.11: {} + hachure-fill@0.5.2: + optional: true + has-bigints@1.1.0: {} has-flag@3.0.0: {} @@ -6629,14 +7826,44 @@ snapshots: he@1.2.0: {} + highlight.js@11.11.1: {} + homedir-polyfill@1.0.3: dependencies: parse-passwd: 1.0.0 hookified@1.15.0: {} + htm@3.1.1: {} + + html-encoding-sniffer@3.0.0: + dependencies: + whatwg-encoding: 2.0.0 + html-tags@3.3.1: {} + htmlparser2@10.1.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.2.2 + entities: 7.0.1 + + http-proxy-agent@5.0.0: + dependencies: + '@tootallnate/once': 2.0.1 + agent-base: 6.0.2 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + https-proxy-agent@5.0.1: + dependencies: + agent-base: 6.0.2 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + human-signals@5.0.0: {} husky@9.1.7: {} @@ -6648,7 +7875,6 @@ snapshots: iconv-lite@0.6.3: dependencies: safer-buffer: 2.1.2 - optional: true ieee754@1.2.1: {} @@ -6659,6 +7885,8 @@ snapshots: image-size@0.5.5: optional: true + immer@10.2.0: {} + import-fresh@3.3.1: dependencies: parent-module: 1.0.1 @@ -6703,6 +7931,12 @@ snapshots: hasown: 2.0.4 side-channel: 1.1.0 + internmap@1.0.1: + optional: true + + internmap@2.0.3: + optional: true + is-array-buffer@3.0.5: dependencies: call-bind: 1.0.8 @@ -6788,6 +8022,8 @@ snapshots: is-plain-object@5.0.0: {} + is-potential-custom-element-name@1.0.1: {} + is-regex@1.2.1: dependencies: call-bound: 1.0.4 @@ -6853,6 +8089,40 @@ snapshots: dependencies: argparse: 2.0.1 + jsdom@19.0.0: + dependencies: + abab: 2.0.6 + acorn: 8.15.0 + acorn-globals: 6.0.0 + cssom: 0.5.0 + cssstyle: 2.3.0 + data-urls: 3.0.2 + decimal.js: 10.6.0 + domexception: 4.0.0 + escodegen: 2.1.0 + form-data: 4.0.6 + html-encoding-sniffer: 3.0.0 + http-proxy-agent: 5.0.0 + https-proxy-agent: 5.0.1 + is-potential-custom-element-name: 1.0.1 + nwsapi: 2.2.24 + parse5: 6.0.1 + saxes: 5.0.1 + symbol-tree: 3.2.4 + tough-cookie: 4.1.4 + w3c-hr-time: 1.0.2 + w3c-xmlserializer: 3.0.0 + webidl-conversions: 7.0.0 + whatwg-encoding: 2.0.0 + whatwg-mimetype: 3.0.0 + whatwg-url: 10.0.0 + ws: 8.21.2 + xml-name-validator: 4.0.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + jsesc@3.1.0: {} json-buffer@3.0.1: {} @@ -6879,6 +8149,11 @@ snapshots: jsonparse@1.3.1: {} + katex@0.16.47: + dependencies: + commander: 8.3.0 + optional: true + keyv@4.5.4: dependencies: json-buffer: 3.0.1 @@ -6887,10 +8162,19 @@ snapshots: dependencies: '@keyv/serialize': 1.1.1 + khroma@2.1.0: + optional: true + kind-of@6.0.3: {} known-css-properties@0.37.0: {} + layout-base@1.0.2: + optional: true + + layout-base@2.0.1: + optional: true + less@4.5.1: dependencies: copy-anything: 2.0.6 @@ -7050,6 +8334,8 @@ snapshots: lodash-es@4.17.23: {} + lodash-es@4.18.1: {} + lodash.camelcase@4.3.0: {} lodash.isplainobject@4.0.6: {} @@ -7115,6 +8401,8 @@ snapshots: semver: 5.7.2 optional: true + markdown-it-fence@0.1.3: {} + markdown-it-image-figures@2.1.1(markdown-it@14.1.0): dependencies: markdown-it: 14.1.0 @@ -7132,6 +8420,15 @@ snapshots: punycode.js: 2.3.1 uc.micro: 2.1.0 + marked-highlight@2.2.4(marked@12.0.2): + dependencies: + marked: 12.0.2 + + marked@12.0.2: {} + + marked@16.4.2: + optional: true + math-intrinsics@1.1.0: {} mathml-tag-names@2.1.3: {} @@ -7181,6 +8478,31 @@ snapshots: merge@2.1.1: {} + mermaid@11.16.1: + dependencies: + '@braintree/sanitize-url': 7.1.2 + '@iconify/utils': 3.1.4 + '@mermaid-js/parser': 1.2.0 + '@types/d3': 7.4.3 + '@upsetjs/venn.js': 2.0.0 + cytoscape: 3.34.0 + cytoscape-cose-bilkent: 4.1.0(cytoscape@3.34.0) + cytoscape-fcose: 2.2.0(cytoscape@3.34.0) + d3: 7.9.0 + d3-sankey: 0.12.3 + dagre-d3-es: 7.0.14 + dayjs: 1.11.21 + dompurify: 3.4.13 + es-toolkit: 1.50.0 + katex: 0.16.47 + khroma: 2.1.0 + marked: 16.4.2 + roughjs: 4.6.6 + stylis: 4.4.0 + ts-dedent: 2.3.0 + uuid: 14.0.1 + optional: true + micromatch@4.0.8: dependencies: braces: 3.0.3 @@ -7223,6 +8545,8 @@ snapshots: nanoid@3.3.11: {} + nanostores@0.11.4: {} + natural-compare@1.4.0: {} needle@3.3.1: @@ -7249,6 +8573,8 @@ snapshots: dependencies: boolbase: 1.0.0 + nwsapi@2.2.24: {} + object-inspect@1.13.4: {} object-keys@1.1.1: {} @@ -7282,6 +8608,21 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.1.1 + omi-transition@0.1.11: + dependencies: + omi: 7.7.13 + + omi-vueify@0.0.12(omi@7.7.13)(vue@3.5.29(typescript@5.9.3)): + dependencies: + omi: 7.7.13 + vue: 3.5.29(typescript@5.9.3) + + omi@7.7.13: + dependencies: + construct-style-sheets-polyfill: 3.0.1 + reactive-signal: 2.0.1 + weakmap-polyfill: 2.0.4 + once@1.4.0: dependencies: wrappy: 1.0.2 @@ -7343,6 +8684,9 @@ snapshots: dependencies: p-limit: 4.0.0 + package-manager-detector@1.8.0: + optional: true + parent-module@1.0.1: dependencies: callsites: 3.1.0 @@ -7358,8 +8702,13 @@ snapshots: parse-passwd@1.0.0: {} + parse5@6.0.1: {} + path-browserify@1.0.1: {} + path-data-parser@0.1.0: + optional: true + path-exists@4.0.0: {} path-exists@5.0.0: {} @@ -7401,6 +8750,15 @@ snapshots: transitivePeerDependencies: - '@vue/composition-api' + points-on-curve@0.2.0: + optional: true + + points-on-path@0.2.1: + dependencies: + path-data-parser: 0.1.0 + points-on-curve: 0.2.0 + optional: true + possible-typed-array-names@1.1.0: {} postcss-less@6.0.0(postcss@8.5.6): @@ -7485,6 +8843,8 @@ snapshots: queue-microtask@1.2.3: {} + reactive-signal@2.0.1: {} + readable-stream@3.6.2: dependencies: inherits: 2.0.4 @@ -7548,6 +8908,9 @@ snapshots: rfdc@1.4.1: {} + robust-predicates@3.0.3: + optional: true + rollup@4.56.0: dependencies: '@types/estree': 1.0.8 @@ -7579,12 +8942,23 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.56.0 fsevents: 2.3.3 + roughjs@4.6.6: + dependencies: + hachure-fill: 0.5.2 + path-data-parser: 0.1.0 + points-on-curve: 0.2.0 + points-on-path: 0.2.1 + optional: true + run-async@2.4.1: {} run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 + rw@1.3.3: + optional: true + rxjs@7.8.2: dependencies: tslib: 2.8.1 @@ -7617,6 +8991,10 @@ snapshots: sax@1.4.4: optional: true + saxes@5.0.1: + dependencies: + xmlchars: 2.2.0 + select@1.1.2: {} semver@5.7.2: @@ -7885,6 +9263,9 @@ snapshots: - supports-color - typescript + stylis@4.4.0: + optional: true + stylus@0.57.0: dependencies: css: 3.0.0 @@ -7923,6 +9304,8 @@ snapshots: csso: 5.0.5 picocolors: 1.1.1 + symbol-tree@3.2.4: {} + synckit@0.11.12: dependencies: '@pkgr/core': 0.2.9 @@ -7935,6 +9318,8 @@ snapshots: string-width: 4.2.3 strip-ansi: 6.0.1 + tailwind-merge@2.6.1: {} + tailwindcss@4.2.1: {} tapable@2.3.0: {} @@ -7944,6 +9329,18 @@ snapshots: '@babel/runtime': 7.28.6 vue: 3.5.29(typescript@5.9.3) + tdesign-icons-vue-next@0.4.7(vue@3.5.29(typescript@5.9.3)): + dependencies: + '@babel/runtime': 7.28.6 + vue: 3.5.29(typescript@5.9.3) + + tdesign-icons-web-components@0.3.7(omi@7.7.13): + dependencies: + '@babel/runtime': 7.28.6 + clsx: 2.1.1 + omi: 7.7.13 + tailwind-merge: 2.6.1 + tdesign-vue-next@1.18.2(vue@3.5.29(typescript@5.9.3)): dependencies: '@babel/runtime': 7.28.6 @@ -7961,6 +9358,49 @@ snapshots: validator: 13.15.26 vue: 3.5.29(typescript@5.9.3) + tdesign-vue-next@1.20.5(vue@3.5.29(typescript@5.9.3)): + dependencies: + '@babel/runtime': 7.28.6 + '@popperjs/core': 2.11.8 + '@types/lodash-es': 4.17.12 + '@types/sortablejs': 1.15.9 + '@types/tinycolor2': 1.4.6 + '@types/validator': 13.15.10 + dayjs: 1.11.19 + lodash-es: 4.18.1 + mitt: 3.0.1 + sortablejs: 1.15.6 + tdesign-icons-vue-next: 0.4.7(vue@3.5.29(typescript@5.9.3)) + tinycolor2: 1.6.0 + validator: 13.15.26 + vue: 3.5.29(typescript@5.9.3) + + tdesign-web-components@1.3.1-alpha.13: + dependencies: + '@babel/runtime': 7.28.6 + '@popperjs/core': 2.11.8 + cherry-markdown: 0.11.9 + class-variance-authority: 0.7.1 + clsx: 2.1.1 + copy-to-clipboard: 3.3.3 + dayjs: 1.11.19 + fast-json-patch: 3.1.1 + htm: 3.1.1 + immer: 10.2.0 + lodash-es: 4.18.1 + markdown-it-fence: 0.1.3 + nanostores: 0.11.4 + omi: 7.7.13 + omi-transition: 0.1.11 + tailwind-merge: 2.6.1 + tdesign-icons-web-components: 0.3.7(omi@7.7.13) + zod: 3.25.76 + transitivePeerDependencies: + - bufferutil + - canvas + - supports-color + - utf-8-validate + text-extensions@2.4.0: {} through@2.3.8: {} @@ -7989,6 +9429,8 @@ snapshots: dependencies: is-number: 7.0.0 + toggle-selection@1.0.6: {} + tough-cookie@4.1.4: dependencies: psl: 1.15.0 @@ -7998,10 +9440,17 @@ snapshots: tr46@0.0.3: {} + tr46@3.0.0: + dependencies: + punycode: 2.3.1 + ts-api-utils@2.4.0(typescript@5.9.3): dependencies: typescript: 5.9.3 + ts-dedent@2.3.0: + optional: true + tsconfig-paths@3.15.0: dependencies: '@types/json5': 0.0.29 @@ -8106,6 +9555,9 @@ snapshots: util-deprecate@1.0.2: {} + uuid@14.0.1: + optional: true + validator@13.15.26: {} vite-svg-loader@5.1.0(vue@3.5.29(typescript@5.9.3)): @@ -8182,14 +9634,42 @@ snapshots: optionalDependencies: typescript: 5.9.3 + w3c-hr-time@1.0.2: + dependencies: + browser-process-hrtime: 1.0.0 + w3c-keyname@2.2.8: {} + w3c-xmlserializer@3.0.0: + dependencies: + xml-name-validator: 4.0.0 + wcwidth@1.0.1: dependencies: defaults: 1.0.4 + weakmap-polyfill@2.0.4: {} + webidl-conversions@3.0.1: {} + webidl-conversions@7.0.0: {} + + whatwg-encoding@2.0.0: + dependencies: + iconv-lite: 0.6.3 + + whatwg-mimetype@3.0.0: {} + + whatwg-url@10.0.0: + dependencies: + tr46: 3.0.0 + webidl-conversions: 7.0.0 + + whatwg-url@11.0.0: + dependencies: + tr46: 3.0.0 + webidl-conversions: 7.0.0 + whatwg-url@5.0.0: dependencies: tr46: 0.0.3 @@ -8267,8 +9747,12 @@ snapshots: ws@7.5.10: {} + ws@8.21.2: {} + xml-name-validator@4.0.0: {} + xmlchars@2.2.0: {} + xss@1.0.15: dependencies: commander: 2.20.3 @@ -8296,6 +9780,8 @@ snapshots: yocto-queue@1.2.2: {} + zod@3.25.76: {} + zrender@6.0.0: dependencies: tslib: 2.3.0 diff --git a/MSLX.WebPanel/src/api/aiApi.ts b/MSLX.WebPanel/src/api/aiApi.ts new file mode 100644 index 00000000..b52fd182 --- /dev/null +++ b/MSLX.WebPanel/src/api/aiApi.ts @@ -0,0 +1,237 @@ +import { HubConnection, HubConnectionBuilder } from '@microsoft/signalr'; +import { request } from '@/utils/request'; +import { TOKEN_NAME, BASE_URL_NAME } from '@/config/global'; +import { getHubUrl } from '@/utils/hub'; + +export interface AiSettingsModel { + aiEnabled: boolean; + aiApiKey: string; + aiBaseUrl: string; + aiModelName: string; +} + +export function getAiSettings() { + return request.get({ + url: '/api/settings/ai', + }); +} + +export function updateAiSettings(data: AiSettingsModel) { + return request.post({ + url: '/api/settings/ai', + data, + }); +} + +export interface ChatMessage { + role: 'user' | 'assistant' | 'system'; + content: string; +} + +let aiChatSignalRConn: HubConnection | null = null; +let activeHttpAbortController: AbortController | null = null; + +export function abortAiChatStream() { + if (activeHttpAbortController) { + activeHttpAbortController.abort(); + activeHttpAbortController = null; + } + if (aiChatSignalRConn) { + try { + aiChatSignalRConn.stop(); + } catch (e) { + console.warn('停止 SignalR 连接:', e); + } + } +} + +export async function sendAiChatStream( + messages: ChatMessage[], + onMessage: (content: string) => void, + onTool: (toolName: string, data: any) => void, + onError: (error: string) => void, + onDone: () => void +) { + try { + const hubUrl = getHubUrl('/api/hubs/aiChatHub'); + const token = localStorage.getItem(TOKEN_NAME) || ''; + + if (!aiChatSignalRConn) { + aiChatSignalRConn = new HubConnectionBuilder() + .withUrl(hubUrl, { + accessTokenFactory: () => token, + }) + .withAutomaticReconnect() + .build(); + } + + if (aiChatSignalRConn.state === 'Disconnected') { + await aiChatSignalRConn.start(); + } + + aiChatSignalRConn.off('ChatChunk'); + aiChatSignalRConn.off('ToolExecuted'); + aiChatSignalRConn.off('ChatError'); + aiChatSignalRConn.off('ChatComplete'); + + aiChatSignalRConn.on('ChatChunk', (chunk: string) => { + onMessage(chunk); + }); + + aiChatSignalRConn.on('ToolExecuted', (toolName: string, data: any) => { + onTool(toolName, data); + }); + + aiChatSignalRConn.on('ChatError', (err: string) => { + onError(err); + onDone(); + }); + + aiChatSignalRConn.on('ChatComplete', () => { + onDone(); + }); + + await aiChatSignalRConn.invoke('SendMessage', messages); + } catch (wsErr: any) { + console.warn('SignalR WebSocket AI 对话尝试失败,回退至 HTTP SSE:', wsErr); + await sendAiChatStreamHttp(messages, onMessage, onTool, onError, onDone); + } +} + +async function sendAiChatStreamHttp( + messages: ChatMessage[], + onMessage: (content: string) => void, + onTool: (toolName: string, data: any) => void, + onError: (error: string) => void, + onDone: () => void +) { + const token = localStorage.getItem(TOKEN_NAME) || ''; + const baseUrl = localStorage.getItem(BASE_URL_NAME) || ''; + const url = `${baseUrl}/api/ai/chat`; + + activeHttpAbortController = new AbortController(); + + try { + const response = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'x-user-token': token, + Authorization: token ? `Bearer ${token}` : '', + }, + signal: activeHttpAbortController.signal, + body: JSON.stringify({ messages }), + }); + + if (!response.ok) { + const errText = await response.text(); + onError(`请求失败 (${response.status}): ${errText}`); + onDone(); + return; + } + + const reader = response.body?.getReader(); + const decoder = new TextDecoder('utf-8'); + if (!reader) { + onError('无法读取响应流'); + onDone(); + return; + } + + let buffer = ''; + while (true) { + const { done, value } = await reader.read(); + if (done) break; + + buffer += decoder.decode(value, { stream: true }); + const events = buffer.split('\n\n'); + buffer = events.pop() || ''; + + for (const evt of events) { + if (!evt.trim()) continue; + const lines = evt.split('\n'); + let eventType = 'message'; + let eventData = ''; + + for (const line of lines) { + if (line.startsWith('event:')) { + eventType = line.substring(6).trim(); + } else if (line.startsWith('data:')) { + eventData = line.substring(5).trim(); + } + } + + const unescapedData = eventData.replace(/\\n/g, '\n'); + + if (eventType === 'message') { + onMessage(unescapedData); + } else if (eventType === 'tool_executed') { + try { + const parsed = JSON.parse(unescapedData); + onTool(parsed.tool, parsed.data); + } catch (e) { + onTool('unknown', unescapedData); + } + } else if (eventType === 'error') { + onError(unescapedData); + } else if (eventType === 'done') { + onDone(); + return; + } + } + } + + onDone(); + } catch (err: any) { + if (err.name !== 'AbortError') { + onError(err.message || '网络连接失败'); + } + onDone(); + } finally { + activeHttpAbortController = null; + } +} + +export interface ConfirmToolResult { + success: boolean; + message: string; + tool?: string; + data?: any; +} + +export async function confirmAiToolAction( + confirmationId: string, + approved: boolean +): Promise { + if (aiChatSignalRConn && aiChatSignalRConn.state === 'Connected') { + try { + const result = await aiChatSignalRConn.invoke('ConfirmToolAction', confirmationId, approved); + if (result && result.success) { + return result; + } + console.warn('SignalR 确认未成功,回退 HTTP:', result); + } catch (e) { + console.warn('SignalR 敏感操作确认失败,回退 HTTP:', e); + } + } + + const token = localStorage.getItem(TOKEN_NAME) || ''; + const baseUrl = localStorage.getItem(BASE_URL_NAME) || ''; + const url = `${baseUrl}/api/ai/confirm-tool`; + + const response = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'x-user-token': token, + Authorization: token ? `Bearer ${token}` : '', + }, + body: JSON.stringify({ confirmationId, approved }), + }); + + const json = await response.json().catch(() => null); + if (!response.ok || !json || json.code !== 200) { + throw new Error(json?.message || `请求失败 (${response.status})`); + } + return json.data; +} diff --git a/MSLX.WebPanel/src/components/AIChat/AIChatDrawer.vue b/MSLX.WebPanel/src/components/AIChat/AIChatDrawer.vue new file mode 100644 index 00000000..5a30e7a1 --- /dev/null +++ b/MSLX.WebPanel/src/components/AIChat/AIChatDrawer.vue @@ -0,0 +1,1206 @@ + + + + + diff --git a/MSLX.WebPanel/src/layouts/components/Header.vue b/MSLX.WebPanel/src/layouts/components/Header.vue index 6013b73f..0ec6128f 100644 --- a/MSLX.WebPanel/src/layouts/components/Header.vue +++ b/MSLX.WebPanel/src/layouts/components/Header.vue @@ -9,6 +9,9 @@ import type { MenuRoute } from '@/types/interface'; import MenuContent from './MenuContent.vue'; import CustomLogo from '@/assets/logo.png'; +import AIChatDrawer from '@/components/AIChat/AIChatDrawer.vue'; + +const aiDrawerVisible = ref(false); const userStore = useUserStore(); @@ -203,6 +206,12 @@ const handleAvatarClick = () => { + + + + + + { + diff --git a/MSLX.WebPanel/src/pages/settings/base/components/AiSettingsCard.vue b/MSLX.WebPanel/src/pages/settings/base/components/AiSettingsCard.vue new file mode 100644 index 00000000..0d279833 --- /dev/null +++ b/MSLX.WebPanel/src/pages/settings/base/components/AiSettingsCard.vue @@ -0,0 +1,193 @@ + + + + + diff --git a/MSLX.WebPanel/src/pages/settings/daemon/index.vue b/MSLX.WebPanel/src/pages/settings/daemon/index.vue index 61ef156d..df909ae3 100644 --- a/MSLX.WebPanel/src/pages/settings/daemon/index.vue +++ b/MSLX.WebPanel/src/pages/settings/daemon/index.vue @@ -5,6 +5,7 @@ import { useWebpanelStore, useUserStore } from '@/store'; import SystemSettingsCard from '../base/components/SystemSettingsCard.vue'; import PluginSlot from '@/components/PluginSlot.vue'; import SslSettingsCard from '../base/components/SslSettingsCard.vue'; +import AiSettingsCard from '../base/components/AiSettingsCard.vue'; const webpanelStore = useWebpanelStore(); const userStore = useUserStore(); @@ -33,6 +34,8 @@ onMounted(() => { + + diff --git a/MSLX.WebPanel/src/utils/hub.ts b/MSLX.WebPanel/src/utils/hub.ts index 310300f0..191b285b 100644 --- a/MSLX.WebPanel/src/utils/hub.ts +++ b/MSLX.WebPanel/src/utils/hub.ts @@ -13,6 +13,7 @@ export function getHubUrl(hubPath: string): string { '/api/hubs/updateProgressHub', '/api/hubs/creationProgressHub', '/api/hubs/instanceControlHub', + '/api/hubs/aiChatHub', ]; const isSlaveRoute = slaveHubs.includes(hubPath) || slaveHubs.some(h => hubPath.startsWith(h)); @@ -23,7 +24,6 @@ export function getHubUrl(hubPath: string): string { finalBase = activeNodeUrl; } - // 拼接 URL 节点ID Token const hubUrl = new URL(hubPath, finalBase); if (token) { hubUrl.searchParams.append('x-user-token', token);