diff --git a/src/processor/.dockerignore b/.dockerignore similarity index 85% rename from src/processor/.dockerignore rename to .dockerignore index 638e37f..ce8a783 100644 --- a/src/processor/.dockerignore +++ b/.dockerignore @@ -1,3 +1,37 @@ +# Include any files or directories that you don't want to be copied to your +# container here (e.g., local build artifacts, temporary files, etc.). +# +# For more help, visit the .dockerignore file reference guide at +# https://docs.docker.com/engine/reference/builder/#dockerignore-file + +**/.DS_Store +**/__pycache__ +**/.venv +**/.classpath +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/bin +**/charts +**/docker-compose* +**/compose* +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/obj +**/secrets.dev.yaml +**/values.dev.yaml +LICENSE +README.md # UV and Python cache directories **/__pycache__/ **/*.py[cod] diff --git a/.flake8 b/.flake8 index 42347c5..1437dc5 100644 --- a/.flake8 +++ b/.flake8 @@ -2,4 +2,4 @@ max-line-length = 88 extend-ignore = E501 exclude = .venv, frontend -ignore = E722,E203, W503, G004, G200, F,E711 \ No newline at end of file +ignore = E722,E203, W503, G004, G200, F,E711,E704 \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..5122f9e --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,9 @@ +{ + "chat.tools.terminal.autoApprove": { + "/^cd H:\\\\Works\\\\Code-Migration\\\\Container-Migration-Solution-Accelerator\\\\src\\\\backend-api ; python -m ruff check src/ --fix 2>&1$/": { + "approve": true, + "matchCommandLine": true + }, + "npx eslint": true + } +} \ No newline at end of file diff --git a/README.md b/README.md index e5b814f..f7bc191 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Container Migration Solution Accelerator -Extract, analyze, and migrate Kubernetes configurations from other cloud providers to Azure Kubernetes Service (AKS) using intelligent multi-agent orchestration powered by Azure OpenAI o3, Semantic Kernel, and MCP (Model Context Protocol) servers. This solution provides automated platform detection, multi-dimensional analysis, and expert-guided configuration transformation with comprehensive migration reporting. +Extract, analyze, and migrate Kubernetes configurations from other cloud providers to Azure Kubernetes Service (AKS) using intelligent multi-agent orchestration powered by Azure OpenAI GPT-5.1, Microsoft Agent Framework, and MCP (Model Context Protocol) servers. This solution provides automated platform detection, multi-dimensional analysis, and expert-guided configuration transformation with comprehensive migration reporting. Transform your Kubernetes workloads with confidence through AI-driven analysis covering security, networking, storage, and Azure Well-Architected Framework principles. Expert agents collaborate to ensure your migrated configurations are production-ready and optimized for Azure. @@ -8,9 +8,9 @@ Transform your Kubernetes workloads with confidence through AI-driven analysis c ## Solution Overview[Solution overview](#solution-overview) -This accelerator provides a complete enterprise migration platform leveraging Azure OpenAI o3 model, Semantic Kernel Process Framework, Azure Container Apps, Azure Blob Storage, Azure Storage Queue, and MCP (Model Context Protocol) servers. The solution consists of a React-based web application for file upload and validation, coupled with an intelligent multi-agent processing engine that analyzes and transforms Kubernetes configurations through event-driven batch processing pipelines. +This accelerator provides a complete enterprise migration platform leveraging Azure OpenAI GPT-5.1, Microsoft Agent Framework workflows, Azure Container Apps, Azure Blob Storage, Azure Storage Queue, and MCP (Model Context Protocol) servers. The solution consists of a React-based web application for file upload and validation, coupled with an intelligent multi-agent processing engine that analyzes and transforms Kubernetes configurations through event-driven batch processing pipelines. -The architecture follows enterprise-grade batch processing patterns with loosely coupled components, enabling organizations to migrate from GKE and EKS to Azure Kubernetes Service at scale while maintaining comprehensive audit trails and expert-level analysis quality. +The architecture follows enterprise-grade batch processing patterns with loosely coupled components, enabling organizations to migrate from managed cloud, enterprise distribution, and self-managed/on-prem Kubernetes platforms (for example: EKS, GKE/Anthos, OpenShift, Rancher/RKE/K3s, Tanzu, and upstream/on-prem Kubernetes) to Azure Kubernetes Service at scale while maintaining comprehensive audit trails and expert-level analysis quality. ### Solution architecture @@ -32,13 +32,13 @@ This solution enables enterprise-grade Kubernetes migration with the following c Complete solution including React web application for file upload, validation, and queue management coupled with intelligent multi-agent processing engine - **Intelligent Platform Detection**
- Automatically identifies source Kubernetes platform (GKE/EKS) through configuration analysis and applies platform-specific migration strategies + Automatically identifies source Kubernetes platform/distribution (for example: EKS, GKE/Anthos, OpenShift, Rancher/RKE/K3s, Tanzu, and self-managed/on-prem Kubernetes) through configuration analysis and applies platform-aware migration strategies - **Multi-Agent Expert Orchestration**
- Specialized agents (Technical Architect, Azure Expert, Platform Experts, QA Engineer) collaborate through Semantic Kernel GroupChat orchestration patterns + Specialized agents (Chief Architect, AKS Expert, platform experts, QA Engineer, Technical Writer, YAML Expert) collaborate using Microsoft Agent Framework group chat orchestration - - **Process Framework Integration**
- Each migration step (analysis, design, conversion, documentation) is built using Semantic Kernel Process Framework with event routing and step orchestration + - **Workflow Engine**
+ Each migration step (analysis, design, conversion, documentation) is executed as a step-based Agent Framework workflow with explicit executor chaining - **MCP Server Tool Integration**
Agents access intelligent tools through Model Context Protocol servers for file operations, knowledge search, and specialized functions without direct model training @@ -51,22 +51,61 @@ This solution enables enterprise-grade Kubernetes migration with the following c ### Agentic Architecture -## +Orchestration flow: 4 workflow executors coordinate group chats and tools. + +```mermaid +flowchart LR + %% Top-level orchestration + telemetry + TELEM["Agent & Process Status
(telemetry)"] + COSMOS[("Cosmos DB
telemetry/state")] + PROC["Process Orchestration
Agent Framework WorkflowBuilder"] + + TELEM --> COSMOS + PROC --- TELEM + + %% Step lanes + subgraph S1["Step 1: Analysis"] + direction TB + S1EXEC["Analysis Executor"] --> S1ORCH["Analysis Chat Orchestrator
(GroupChatOrchestrator)"] --> S1AGENTS["Agents:
Chief Architect
AKS Expert
Platform experts (EKS/GKE/OpenShift/Rancher/Tanzu/OnPremK8s)"] + end + + subgraph S2["Step 2: Design"] + direction TB + S2EXEC["Design Executor"] --> S2ORCH["Design Chat Orchestrator
(GroupChatOrchestrator)"] --> S2AGENTS["Agents:
Chief Architect
AKS Expert
Platform experts (EKS/GKE/OpenShift/Rancher/Tanzu/OnPremK8s)"] + end + + subgraph S3["Step 3: YAML Conversion"] + direction TB + S3EXEC["Convert Executor"] --> S3ORCH["YAML Chat Orchestrator
(GroupChatOrchestrator)"] --> S3AGENTS["Agents:
YAML Expert
Azure Architect
AKS Expert
QA Engineer
Chief Architect"] + end + + subgraph S4["Step 4: Documentation"] + direction TB + S4EXEC["Documentation Executor"] --> S4ORCH["Documentation Chat Orchestrator
(GroupChatOrchestrator)"] --> S4AGENTS["Agents:
Technical Writer
Azure Architect
AKS Expert
Chief Architect
Platform experts (EKS/GKE/OpenShift/Rancher/Tanzu/OnPremK8s)"] + end + + PROC --> S1 + S1 -->|Analysis Result| S2 + S2 -->|Design Result| S3 + S3 -->|YAML Converting Result| S4 + +``` + If you want to get know more detail about Agentic Architecture, please take a look at this document: [Agentic Architecture](docs/AgenticArchitecture.md) ### Technical implementation highlights **Advanced AI Orchestration Patterns:** -- **Model**: Azure OpenAI o3 reasoning model for advanced reasoning and analysis capabilities -- **Framework**: Semantic Kernel multi-agent orchestration with GroupChat patterns -- **Process Management**: Semantic Kernel Process Framework for step-by-step workflow orchestration +- **Model**: Azure OpenAI GPT-5.1 for advanced reasoning and analysis capabilities +- **Framework**: Microsoft Agent Framework for multi-agent orchestration and workflow execution +- **Workflow Management**: Agent Framework `WorkflowBuilder` with step executors (analysis → design → yaml → documentation) - **Tool Access**: MCP (Model Context Protocol) servers enabling intelligent tool selection and usage **MCP Server Integration:** -- **Internal MCP Servers**: Blob I/O Operations (FastMCP), DateTime Operations (FastMCP) -- **Microsoft MCP Server**: Document search and knowledge retrieval -- **Intelligent Tool Selection**: Agents autonomously choose appropriate tools based on context +- **Internal MCP Servers (FastMCP)**: Blob I/O, DateTime, Mermaid validation, YAML inventory +- **External MCP Servers**: Microsoft Learn Docs (HTTP) and Fetch (`uvx mcp-server-fetch`) +- **Intelligent Tool Selection**: Agents choose tools based on context ## Resources @@ -88,7 +127,7 @@ If you'd like to customize the solution accelerator, here are some common areas [Multi-Agent Orchestration Approach](docs/MultiAgentOrchestration.md) -[Process Framework Implementation](docs/ProcessFrameworkGuide.md) +[Workflow Implementation Guide](docs/ProcessFrameworkGuide.md) [MCP Server Integration Guide](docs/MCPServerGuide.md) @@ -107,10 +146,94 @@ The Container Migration Solution Accelerator supports development and deployment - [Local Development Setup Guide](docs/LocalDevelopmentSetup.md) - Comprehensive setup instructions for Windows, Linux, and macOS - Includes native Windows setup, WSL2 configuration, and cross-platform development tools -![Deployment Architecture](docs/images/readme/deployment-architecture.png) - -> ⚠️ **Important: Check Azure OpenAI o3 Model Availability** -> To ensure o3 model access is available in your subscription, please check [Azure OpenAI model availability](https://learn.microsoft.com/azure/ai-services/openai/concepts/models#o3-models) before you deploy the solution. +```mermaid +flowchart TB + %% --- Themed deployment view (v2) --- + %% Palette intentionally reuses colors already present in repo diagrams. + + subgraph CLIENT["Client"] + direction TB + U["User / Browser"] + end + + subgraph RUNTIME["Runtime"] + direction TB + + subgraph APPS["Azure Container Apps"] + direction TB + FE["Frontend"] + API["Backend API"] + PROC["Processor (Queue Worker)"] + end + + subgraph DATA["Data & Configuration"] + direction TB + Q[("Azure Storage Queue")] + BLOB[("Azure Blob Storage")] + COSMOS[("Azure Cosmos DB")] + APP_CFG[("Azure App Configuration")] + end + end + + subgraph AI["AI"] + direction TB + AOAI[("Azure OpenAI
Azure AI Foundry
GPT-5.1")] + end + + subgraph BUILD["Images & Identity"] + direction TB + ACR[("Azure Container Registry")] + ID["User Assigned Managed Identity"] + end + + %% User flow + U -->|HTTPS request| FE + FE -->|HTTPS request| API + + %% Job intake + processing + API -->|Enqueue job| Q + Q -->|Dequeue job| PROC + + %% Artifacts + state + PROC -->|Write artifacts| BLOB + PROC -->|Write state| COSMOS + + %% Configuration + API -->|Read config| APP_CFG + PROC -->|Read config| APP_CFG + + %% LLM usage + PROC -->|LLM call| AOAI + + %% Image pulls + ACR -->|Pull image| FE + ACR -->|Pull image| API + ACR -->|Pull image| PROC + + %% Identity usage + ID -.-> FE + ID -.-> API + ID -.-> PROC + + %% Theme styling (monotone + subtle accents) + classDef core fill:#ffffff,stroke:#111827,color:#111827,stroke-width:1px; + classDef store fill:#e0f2fe,stroke:#0284c7,color:#111827,stroke-width:1px; + classDef frame fill:#f8fafc,stroke:#94a3b8,color:#111827,stroke-width:1px; + + class U,FE,API,PROC core; + class Q,BLOB,COSMOS,APP_CFG,AOAI,ACR store; + class ID core; + + style CLIENT fill:#f8fafc,stroke:#94a3b8,color:#111827; + style RUNTIME fill:#f8fafc,stroke:#94a3b8,color:#111827; + style APPS fill:#ffffff,stroke:#94a3b8,color:#111827; + style DATA fill:#ffffff,stroke:#94a3b8,color:#111827; + style AI fill:#e0f2fe,stroke:#0284c7,color:#111827; + style BUILD fill:#e0f2fe,stroke:#0284c7,color:#111827; +``` + +> ⚠️ **Important: Check Azure OpenAI GPT-5.1 Availability** +> Model availability and quotas vary by region and subscription. Check the Azure OpenAI models catalog before deploying: https://learn.microsoft.com/azure/ai-services/openai/concepts/models ### Prerequisites and costs @@ -118,13 +241,13 @@ To deploy this solution accelerator, ensure you have access to an [Azure subscri **Required Azure Services:** -| Service | Description | Pricing | -| ----------------------- | ------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | -| Azure OpenAI Service | Provides REST API access to OpenAI's o3 reasoning model for advanced reasoning and analysis | [Pricing](https://azure.microsoft.com/pricing/details/cognitive-services/openai-service/) | -| Azure Container Apps | Runs containerized migration processor without managing infrastructure | [Pricing](https://azure.microsoft.com/pricing/details/container-apps/) | -| Azure Blob Storage | Stores source configurations, processed files, and migration reports | [Pricing](https://azure.microsoft.com/pricing/details/storage/blobs/) | -| Azure App Configuration | Manages application settings and agent configurations | [Pricing](https://azure.microsoft.com/pricing/details/app-configuration/) | -| Azure Queue Storage | Handles event-driven processing and agent communication | [Pricing](https://azure.microsoft.com/pricing/details/storage/queues/) | +| Service | Description | Pricing | +| ----------------------- | --------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | +| Azure OpenAI Service | Provides REST API access to the GPT-5.1 model for advanced reasoning and analysis | [Pricing](https://azure.microsoft.com/pricing/details/cognitive-services/openai-service/) | +| Azure Container Apps | Runs containerized migration processor without managing infrastructure | [Pricing](https://azure.microsoft.com/pricing/details/container-apps/) | +| Azure Blob Storage | Stores source configurations, processed files, and migration reports | [Pricing](https://azure.microsoft.com/pricing/details/storage/blobs/) | +| Azure App Configuration | Manages application settings and agent configurations | [Pricing](https://azure.microsoft.com/pricing/details/app-configuration/) | +| Azure Queue Storage | Handles event-driven processing and agent communication | [Pricing](https://azure.microsoft.com/pricing/details/storage/queues/) | Pricing varies per region and usage, so it isn't possible to predict exact costs for your usage. The majority of the Azure resources used in this infrastructure are on usage-based pricing tiers. @@ -132,15 +255,15 @@ Use the [Azure pricing calculator](https://azure.microsoft.com/pricing/calculato **Model Access Requirements:** -- **o3 Model**: Available across **20+ Azure regions** including Australia East, Brazil South, Canada East, East US, East US2, France Central, Germany West Central, Italy North, Japan East, Korea Central, North Central US, Norway East, Poland Central, South Africa North, South Central US, South India, Spain Central, Sweden Central, Switzerland North, UAE North, UK South, West Europe, West US, and West US3 -- **Registration Requirements**: Some models may require registration for access -- **Quota Management**: Ensure sufficient TPM (tokens per minute) quota for batch processing +- **Availability varies**: GPT-5.1 availability may vary by region and subscription. +- **Registration requirements**: Registration is required for access to `gpt-5.1`, `gpt-5.1-codex`, and `gpt-5.1-codex-max`. Request access here: [https://aka.ms/oai/gpt5access](https://aka.ms/oai/gpt5access). For region availability details, see [Model summary table and region availability](https://learn.microsoft.com/en-us/azure/ai-foundry/foundry-models/concepts/models-sold-directly-by-azure?view=foundry-classic&tabs=global-standard-aoai%2Cstandard-chat-completions%2Cglobal-standard&pivots=azure-openai#model-summary-table-and-region-availability). +- **Quota management**: Ensure sufficient quota for batch processing. ## Guidance ## [Migration Scenario](#migration-scenario) -A DevOps engineer at a multi-cloud enterprise manages Kubernetes workloads across GKE, EKS, and needs to migrate critical applications to Azure Kubernetes Service (AKS) following company cloud consolidation strategy. +A DevOps engineer at a multi-platform enterprise manages Kubernetes workloads across managed cloud and self-managed/on-prem clusters (for example: GKE, EKS, and on-prem Kubernetes) and needs to migrate critical applications to Azure Kubernetes Service (AKS) following company cloud consolidation strategy. The engineer has dozens of complex Kubernetes configurations with intricate networking, storage, and security requirements. Manual migration analysis would take weeks and risk configuration errors that could impact production workloads. @@ -152,18 +275,18 @@ Using the Migration Solution Accelerator, the complete processing flow works as 2. **Automated File Inspection**: Web application performs comprehensive validation: - File format and content-type verification - YAML syntax validation - - Platform consistency checks (prevents GKE/EKS mixing) + - Platform consistency checks (prevents mixing incompatible source platforms in a single batch) 3. **Queue Generation**: After successful inspection, the system generates processing jobs with unique identifiers and submits them to Azure Storage Queue 4. **Migration Processor Activation**: The multi-agent migration processor (this solution) monitors the queue, picks up processing jobs, and begins intelligent analysis -> ⚠️ **Important Note on Processing**: This is a **synchronous system**. Users can upload either a single file or multiple files for processing, but only one migration batch runs at a time. Once a batch has started, users must wait for it to complete before the next upload begins. If additional files are uploaded while a migration is in progress, they will automatically queue and start only after the current batch finishes. +> ⚠️ **Important Note on Processing**: Processing is **queue-driven**. Concurrency is configurable (default is a single worker), so uploads may run sequentially or in parallel depending on deployment settings. ### **AI-Powered Migration Process** Once the migration processor receives a queue message, expert AI agents automatically: -1. **Platform Detection**: Identify whether configurations are from GKE or EKS based on analyzed content -2. **Multi-Expert Analysis**: Technical Architect analyzes overall architecture, while platform-specific experts (GKE/EKS) identify migration challenges and Azure Expert applies Well-Architected Framework principles +1. **Platform Detection**: Identify the most likely source Kubernetes platform/distribution (e.g., EKS/GKE/OpenShift/Rancher/Tanzu/On-prem) based on analyzed content +2. **Multi-Expert Analysis**: Technical Architect analyzes overall architecture, while the matching platform expert(s) identify migration challenges and Azure Expert applies Well-Architected Framework principles 3. **Quality Validation**: QA Engineer validates transformation logic and identifies potential issues 4. **YAML Transformation**: Expert YAML agent converts configurations with security, networking, and storage optimizations 5. **Comprehensive Documentation**: Generate detailed migration reports capturing all expert insights and transformation decisions @@ -179,7 +302,7 @@ The solution provides enterprise-grade capabilities: Thanks to this enterprise batch processing architecture and AI-powered multi-agent orchestration, migrations that previously took weeks are completed in hours with higher accuracy, comprehensive documentation, and full audit trails. -> ⚠️ **Note**: This solution uses Azure OpenAI o3 reasoning model for advanced reasoning. The model is available across 20+ Azure regions globally. Registration may be required for certain models. Sample configurations in this repository are for demonstration purposes. +> ⚠️ **Note**: This solution uses Azure OpenAI GPT-5.1 for advanced reasoning. Model availability and access requirements vary by region and subscription. Sample configurations in this repository are for demonstration purposes. ### Business value @@ -196,28 +319,79 @@ This solution provides significant value through intelligent automation: ### Multi-agent orchestration architecture -This solution implements advanced multi-agent patterns using Semantic Kernel GroupChat orchestration: +This solution implements advanced multi-agent patterns using Microsoft Agent Framework group chat orchestration: **Expert Agent Specializations:** - **Technical Architect**: Overall architecture analysis and design decisions - **Azure Expert**: Azure-specific optimizations and Well-Architected Framework compliance -- **GKE Expert**: Google Kubernetes Engine specific knowledge and migration patterns +- **GKE Expert**: Google Kubernetes Engine (GKE/Anthos) specific knowledge and migration patterns - **EKS Expert**: Amazon Elastic Kubernetes Service expertise and AWS-to-Azure translations +- **OpenShift Expert**: Red Hat OpenShift specific patterns and transformations +- **Rancher Expert**: Rancher/RKE/RKE2/K3s patterns and migration considerations +- **Tanzu Expert**: VMware Tanzu/TKG patterns and migration considerations +- **OnPremK8s Expert**: Upstream/self-managed/on-prem Kubernetes patterns and common on-prem dependencies - **QA Engineer**: Validation, testing strategies, and quality assurance - **YAML Expert**: Configuration transformation and syntax optimization -**Process Framework Integration:** -Each migration phase is implemented as Semantic Kernel Process Framework steps with event routing: - -![Process Flow](docs/images/readme/process_flow.png) +**Workflow Integration:** +Each migration step is implemented as an Agent Framework workflow with explicit executor chaining: + +```mermaid +flowchart TB + %% Migration flow (step-oriented) + + subgraph WF[" "] + direction TB + + subgraph ROW[" "] + direction LR + + subgraph A["Analysis Process"] + direction TB + A1["• Platform Detection
• Technical Architecture Review
• Source Configuration Analysis
• Migration Complexity Assessment"] + A2["Agents:
• Chief Architect
• AKS Expert
• Platform Expert(s)"] + A1 --> A2 + end + + subgraph D["Design Process"] + direction TB + D1["• Azure Well-Architected Framework
• Target Architecture Design
• Service Mapping Strategy
• Security & Compliance Review"] + D2["Agents:
• Chief Architect
• AKS Expert
• Platform Expert(s)"] + D1 --> D2 + end + + subgraph C["Conversion Process"] + direction TB + C1["• YAML Transformation
• Azure Service Configuration
• Resource Optimization
• Validation & Testing"] + C2["Agents:
• Chief Architect
• Azure Expert
• AKS Expert
• YAML Expert
• QA Engineer"] + C1 --> C2 + end + + subgraph DOC["Documentation Process"] + direction TB + DOC1["• Migration Report Generation
• Expert Recommendations
• Implementation Guide
• Post-migration Checklist"] + DOC2["Agents:
• Technical Writer
• All Experts"] + DOC1 --> DOC2 + end + + A -->|Architecture Insights| D + D -->|Design Specifications| C + C -->|Converted Configurations| DOC + end + + end +``` **MCP Server Integration:** Agents access tools through Model Context Protocol servers for intelligent capability extension: -- **Blob Operations MCP**: File reading, writing, and management operations -- **DateTime MCP**: Time-based operations and scheduling -- **Microsoft Docs MCP**: Azure documentation and best practices knowledge +- **Blob Operations MCP**: File reading/writing and artifact management (Azure Blob Storage) +- **Microsoft Learn Docs MCP (HTTP)**: Documentation search/retrieval and best-practices lookup +- **DateTime MCP**: Timestamp generation and time-based operations +- **Fetch MCP**: URL fetching for validation (for example: verifying references) +- **Mermaid Validation MCP**: Validate Mermaid diagrams generated during design/docs steps +- **YAML Inventory MCP**: Enumerate converted YAML objects for runbooks and reports ### Cross references @@ -227,7 +401,6 @@ Check out related Microsoft solution accelerators: | --------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | | [Content Processing Solution Accelerator](https://github.com/microsoft/content-processing-solution-accelerator) | Process and extract data from unstructured documents using AI | | [Document Knowledge Mining](https://github.com/microsoft/Document-Knowledge-Mining-Solution-Accelerator) | Extract insights from documents with AI-powered search | -| [Semantic Kernel Multi-Agent Samples](https://github.com/microsoft/semantic-kernel/tree/main/dotnet/samples) | Additional multi-agent orchestration patterns | ## Provide feedback Have questions, find a bug, or want to request a feature? [Submit a new issue](https://github.com/microsoft/container-migration-solution-accelerator/issues) on this repo and we'll connect. diff --git a/TRANSPARENCY_FAQ.md b/TRANSPARENCY_FAQ.md index bc9d829..7cf8005 100644 --- a/TRANSPARENCY_FAQ.md +++ b/TRANSPARENCY_FAQ.md @@ -2,7 +2,7 @@ ## What is the Container Migration Solution Accelerator? -This solution accelerator is an open-source GitHub Repository designed to streamline the migration of Kubernetes workloads from various container platforms (EKS, GKE, etc.) to Azure Kubernetes Service (AKS). It automates the analysis, design, configuration transformation, and documentation generation processes to enhance the speed and accuracy of container platform migrations. The solution is built using Azure OpenAI Service, Semantic Kernel Process Framework, Model Context Protocol (MCP) servers, and Azure services integration. +This solution accelerator is an open-source GitHub Repository designed to streamline the migration of Kubernetes workloads from various container platforms (EKS, GKE, etc.) to Azure Kubernetes Service (AKS). It automates the analysis, design, configuration transformation, and documentation generation processes to enhance the speed and accuracy of container platform migrations. The solution is built using Azure OpenAI Service, Microsoft Agent Framework (workflow + group chat orchestration), Model Context Protocol (MCP) servers, and Azure services integration. ## What can the Container Migration Solution Accelerator do? @@ -40,7 +40,7 @@ The solution was evaluated using multiple approaches: 3. **Migration Coverage**: Testing across different source platforms (EKS, GKE) and various workload types (stateless applications, databases, monitoring systems, etc.). -4. **Process Framework Reliability**: Evaluation of the Semantic Kernel Process Framework's error handling, retry mechanisms, and failure recovery capabilities. +4. **Process Framework Reliability**: Evaluation of the Agent Framework workflow's error handling, retry mechanisms, and failure recovery capabilities. 5. **Documentation Quality**: Assessment of generated documentation for completeness, accuracy, and actionability. diff --git a/docs/AgenticArchitecture.md b/docs/AgenticArchitecture.md index 732a34f..132e36a 100644 --- a/docs/AgenticArchitecture.md +++ b/docs/AgenticArchitecture.md @@ -1,110 +1,84 @@ # Agentic Architecture - Container Migration Solution Accelerator -Based on your actual implementation, here's the comprehensive agentic architecture that mirrors the style of your reference image: +High-level view of how the 4-step orchestration works (executors, group chat orchestrators, and tools). ## Architecture Overview ```mermaid -graph TB - subgraph "Entry Layer" - WEB[Web App/Queue] - SERVICE[Migration Service] +flowchart LR + %% Top-level orchestration + telemetry + TELEM["Agent & Process Status
(telemetry)"] + COSMOS[("Cosmos DB
telemetry/state")] + PROC["Process Orchestration
Agent Framework WorkflowBuilder"] + + TELEM --> COSMOS + PROC --- TELEM + + %% Step lanes + subgraph S1["Step 1: Analysis"] + direction TB + S1EXEC["Analysis Executor"] --> S1ORCH["Analysis Chat Orchestrator
(GroupChatOrchestrator)"] --> S1AGENTS["Agents:
Chief Architect
AKS Expert
Platform experts (EKS/GKE/OpenShift/Rancher/Tanzu/OnPremK8s)"] end - subgraph "Process Engine" - PROC[Process Orchestrator
Semantic Kernel] + subgraph S2["Step 2: Design"] + direction TB + S2EXEC["Design Executor"] --> S2ORCH["Design Chat Orchestrator
(GroupChatOrchestrator)"] --> S2AGENTS["Agents:
Chief Architect
AKS Expert
Platform experts (EKS/GKE/OpenShift/Rancher/Tanzu/OnPremK8s)"] end - subgraph "Migration Steps" - ANALYSIS[Analysis Step
Platform Discovery] - DESIGN[Design Step
Azure Architecture] - YAML[YAML Step
Configuration Transform] - DOCS[Documentation Step
Report Generation] + subgraph S3["Step 3: YAML Conversion"] + direction TB + S3EXEC["Convert Executor"] --> S3ORCH["YAML Chat Orchestrator
(GroupChatOrchestrator)"] --> S3AGENTS["Agents:
YAML Expert
Azure Architect
AKS Expert
QA Engineer
Chief Architect"] end - subgraph "AI Agents (7 Specialists)" - AGENTS[Multi-Agent System
• Technical Architect
• Azure Expert
• EKS/GKE Experts
• QA Engineer
• Technical Writer
• YAML Expert] + subgraph S4["Step 4: Documentation"] + direction TB + S4EXEC["Documentation Executor"] --> S4ORCH["Documentation Chat Orchestrator
(GroupChatOrchestrator)"] --> S4AGENTS["Agents:
Technical Writer
Azure Architect
AKS Expert
Chief Architect
Platform experts (EKS/GKE/OpenShift/Rancher/Tanzu/OnPremK8s)"] end - subgraph "Tool Layer" - MCP[MCP Servers
• Blob Storage
• Microsoft Docs
• DateTime Utils] - end - - subgraph "Storage Layer" - STORAGE[Azure Services
• Blob Storage
• Cosmos DB
• OpenAI GPT o3] - end + PROC --> S1 + S1 -->|Analysis Result| S2 + S2 -->|Design Result| S3 + S3 -->|YAML Converting Result| S4 - %% Main Flow - WEB --> SERVICE - SERVICE --> PROC - PROC --> ANALYSIS - ANALYSIS --> DESIGN - DESIGN --> YAML - YAML --> DOCS - - %% AI Integration - ANALYSIS -.-> AGENTS - DESIGN -.-> AGENTS - YAML -.-> AGENTS - DOCS -.-> AGENTS - - %% Tool Access - AGENTS -.-> MCP - MCP -.-> STORAGE - - %% Styling for better readability - classDef entryLayer fill:#e3f2fd,stroke:#1976d2,stroke-width:3px,color:#000 - classDef processLayer fill:#fff3e0,stroke:#f57c00,stroke-width:3px,color:#000 - classDef stepLayer fill:#f3e5f5,stroke:#7b1fa2,stroke-width:3px,color:#000 - classDef agentLayer fill:#e8f5e8,stroke:#388e3c,stroke-width:3px,color:#000 - classDef toolLayer fill:#fce4ec,stroke:#c2185b,stroke-width:3px,color:#000 - classDef storageLayer fill:#e1f5fe,stroke:#0288d1,stroke-width:3px,color:#000 - - class WEB,SERVICE entryLayer - class PROC processLayer - class ANALYSIS,DESIGN,YAML,DOCS stepLayer - class AGENTS agentLayer - class MCP toolLayer - class STORAGE storageLayer ``` ## Agent Specialization by Phase ### Analysis Phase Agents -- **Technical Architect**: Leads overall analysis strategy and coordination -- **EKS Expert**: Identifies AWS EKS-specific patterns and configurations -- **GKE Expert**: Identifies Google GKE-specific patterns and configurations +- **Chief Architect**: Leads overall analysis strategy and coordination +- **AKS Expert**: Reviews for AKS/Azure migration readiness +- **Platform experts**: Registry-loaded participants (EKS/GKE/OpenShift/Rancher/Tanzu/OnPremK8s); coordinator keeps non-matching experts quiet ### Design Phase Agents -- **Technical Architect**: Defines migration architecture patterns -- **Azure Expert**: Designs Azure service mappings and optimizations -- **EKS Expert**: Provides source platform context for AWS workloads -- **GKE Expert**: Provides source platform context for GCP workloads +- **Chief Architect**: Defines migration architecture patterns and reconciles trade-offs +- **AKS Expert**: Ensures AKS-specific conventions and constraints are applied +- **Platform experts**: Provide source-platform context and constraints for the detected platform ### YAML Conversion Phase Agents - **YAML Expert**: Performs configuration transformations and syntax optimization -- **Azure Expert**: Ensures Azure service integration and compliance +- **Azure Architect**: Ensures Azure service integration and compliance +- **AKS Expert**: Ensures converted manifests align with AKS expectations - **QA Engineer**: Validates converted configurations and tests -- **Technical Writer**: Documents conversion decisions and generates reports +- **Chief Architect**: Provides overall review and integration ### Documentation Phase Agents -- **Technical Architect**: Provides architectural documentation and migration summary -- **Azure Expert**: Documents Azure-specific configurations and optimizations -- **EKS/GKE Experts**: Document source platform analysis and transformation logic -- **QA Engineer**: Provides validation reports and testing documentation - **Technical Writer**: Creates comprehensive migration documentation +- **Azure Architect**: Documents Azure-specific configurations and optimizations +- **AKS Expert**: Documents AKS-focused implementation guidance and caveats +- **Chief Architect**: Provides architectural documentation and migration summary +- **Platform experts**: Document source platform analysis and transformation logic ## Data Flow Architecture ### Input Processing -1. **Queue Service** receives migration requests from web app or direct API -2. **Migration Service** processes queue messages and initiates migration process -3. **Process Orchestrator** manages step-by-step execution with event routing +1. **Web app** creates a migration request +2. **Queue worker service** receives the migration request from **Azure Storage Queue** +3. **Migration Processor** runs the end-to-end workflow (analysis → design → yaml → documentation) ### Step Execution Pattern @@ -121,11 +95,13 @@ Each step follows this pattern: ### MCP Server Integration -All agents have access to Model Context Protocol (MCP) servers via Semantic Kernel plugin: +All agents have access to Model Context Protocol (MCP) servers via Microsoft Agent Framework tool abstractions: - **Blob Operations**: File reading/writing to Azure Blob Storage - **Microsoft Docs**: Azure documentation lookup and best practices - **DateTime Utilities**: Timestamp generation and time-based operations +- **Fetch**: URL fetching for validation (e.g., verifying references) +- **YAML Inventory**: Enumerate converted YAML objects for runbooks ## Key Architectural Principles @@ -140,22 +116,27 @@ Each step has a focused objective: ### Event-Driven Orchestration -Steps communicate through Semantic Kernel events: - -- `StartMigration` → Analysis Step -- `AnalysisCompleted` → Design Step -- `DesignCompleted` → YAML Step -- `YamlCompleted` → Documentation Step +Steps are executed as a directed workflow (with start node and edges) using the Agent Framework workflow engine. +The processor emits workflow/executor events for observability and telemetry. ### Multi-Agent Collaboration -Within each step, specialized agents collaborate through GroupChat orchestration: +Within each step, specialized agents collaborate through group chat orchestration: - Structured conversation patterns - Domain expertise contribution - Consensus building on decisions - Quality validation and review +### Evaluation and Quality Checks + +The processor uses multiple quality signals to reduce regressions and increase reliability: + +- **Typed step outputs**: workflow executors and orchestrators exchange typed models per step (analysis → design → yaml → documentation). +- **QA sign-offs**: the QA agent focuses on validation steps and flags missing/unsafe transformations. +- **Tool-backed validation**: steps can call validation tools via MCP (e.g., Mermaid validation, YAML inventory grounding, docs lookups). +- **Unit tests**: processor unit tests live under [src/processor/src/tests/unit/](../src/processor/src/tests/unit/). + ### Tool-Enabled Intelligence Agents access external capabilities through MCP servers: @@ -176,41 +157,27 @@ Comprehensive tracking throughout the process: ## File Location Mapping ```text -src/ -├── main_service.py # Queue Service Entry Point -├── services/migration_service.py # Migration Orchestration -├── libs/processes/ -│ └── aks_migration_process.py # Process Framework Definition -├── libs/steps/ -│ ├── analysis_step.py # Analysis Step Implementation -│ ├── design_step.py # Design Step Implementation -│ ├── yaml_step.py # YAML Step Implementation -│ └── documentation_step.py # Documentation Step Implementation -├── libs/steps/orchestration/ -│ ├── analysis_orchestration.py # Analysis Agent Orchestration -│ ├── design_orchestration.py # Design Agent Orchestration -│ ├── yaml_orchestration.py # YAML Agent Orchestration -│ └── documentation_orchestration.py # Documentation Agent Orchestration -├── agents/ -│ ├── technical_architect/agent_info.py -│ ├── azure_expert/agent_info.py -│ ├── eks_expert/agent_info.py -│ ├── gke_expert/agent_info.py -│ ├── qa_engineer/agent_info.py -│ ├── technical_writer/agent_info.py -│ └── yaml_expert/agent_info.py -└── plugins/mcp_server/ - ├── MCPBlobIOPlugin.py # Azure Blob Storage MCP Server - ├── MCPMicrosoftDocs.py # Microsoft Docs MCP Server - └── MCPDatetimePlugin.py # DateTime Utilities MCP Server +src/processor/src/ +├── main_service.py # Queue worker entry point +├── services/queue_service.py # Azure Storage Queue consumer +├── services/control_api.py # Control API (health/kill) +├── services/process_control.py # Process control store/manager +├── steps/migration_processor.py # WorkflowBuilder + step chaining +├── steps/analysis/workflow/analysis_executor.py +├── steps/design/workflow/design_executor.py +├── steps/convert/workflow/yaml_convert_executor.py +└── steps/documentation/ + ├── orchestration/documentation_orchestrator.py + ├── workflow/documentation_executor.py + └── agents/ # Agent prompt files ``` ## Summary This architecture implements a sophisticated agentic system that combines: -- **Semantic Kernel Process Framework** for structured workflow execution -- **Multi-Agent GroupChat Orchestration** for domain expertise collaboration +- **Microsoft Agent Framework Workflow** for structured workflow execution +- **Multi-Agent Group Chat Orchestration** for domain expertise collaboration - **Model Context Protocol (MCP)** for tool integration and external system access - **Azure Cloud Services** for scalable storage and data management - **Event-Driven Architecture** for loose coupling and reliability diff --git a/docs/ConfigureMCPServers.md b/docs/ConfigureMCPServers.md index 546c1b2..b8c9694 100644 --- a/docs/ConfigureMCPServers.md +++ b/docs/ConfigureMCPServers.md @@ -4,7 +4,7 @@ This guide explains how to configure and customize Model Context Protocol (MCP) ## Overview -The Container Migration Solution Accelerator implements a sophisticated MCP architecture that separates client plugins from server implementations, enabling secure, scalable, and maintainable tool integration for AI agents. +The Container Migration Solution Accelerator implements a sophisticated MCP architecture that separates Agent Framework MCP tools (clients) from server implementations, enabling secure, scalable, and maintainable tool integration for AI agents. ### MCP Architecture Benefits @@ -20,8 +20,8 @@ The Container Migration Solution Accelerator implements a sophisticated MCP arch The solution integrates MCP through multiple patterns: -- **Stdio Plugins**: Local MCP servers spawned as subprocesses (blob, file, datetime operations) -- **HTTP Plugins**: Remote MCP servers accessed via HTTP (Microsoft documentation) +- **Stdio Tools**: Local MCP servers spawned as subprocesses (fetch, blob, datetime, mermaid validation, YAML inventory) +- **HTTP Tools**: Remote MCP servers accessed via HTTP (Microsoft Learn documentation) - **Context Management**: Unified context sharing across all expert agents - **Tool Discovery**: Dynamic tool registration and capability discovery - **Error Handling**: Robust error handling with fallback mechanisms @@ -29,34 +29,38 @@ The solution integrates MCP through multiple patterns: ### MCP Server Structure ```text -src/plugins/mcp_server/ +src/processor/src/libs/mcp_server/ ├── __init__.py -├── MCPBlobIOPlugin.py # Azure Blob Storage operations - MCP Server Client -├── MCPDatetimePlugin.py # Date/time utilities - MCP Server Client -├── MCPMicrosoftDocs.py # Microsoft documentation API - MCP Server Client -├── mcp_blob_io_operation/ # Blob storage MCP Server Implementation (FastMCP) -│ ├── credential_util.py -│ └── mcp_blob_io_operation.py -└── mcp_datetime/ # Datetime utilities MCP Server Implementation (FastMCP) - └── mcp_datetime.py +├── MCPBlobIOTool.py # Azure Blob Storage MCP tool wrapper +├── MCPDatetimeTool.py # Date/time utilities MCP tool wrapper +├── MCPMicrosoftDocs.py # Microsoft Learn MCP tool wrapper (HTTP) +├── MCPMermaidTool.py # Mermaid validation MCP tool wrapper +├── MCPYamlInventoryTool.py # YAML inventory MCP tool wrapper +├── blob_io_operation/ # Blob storage FastMCP server implementation +├── datetime/ # Datetime FastMCP server implementation +├── mermaid/ # Mermaid FastMCP server implementation +└── yaml_inventory/ # YAML inventory FastMCP server implementation ``` **Architecture Notes:** -- **Client Plugin Files**: Main MCP client plugins that connect to MCP servers via Semantic Kernel -- **Server Implementation Folders**: Contains the actual FastMCP server implementations that provide the tools +- **Tool Wrapper Files**: Agent Framework MCP tools (stdio/http) used by agents and orchestrators +- **Server Implementation Folders**: FastMCP server implementations that provide the tool endpoints - **Credential Utilities**: Shared authentication and credential management for Azure services -- **Process Architecture**: Client plugins spawn server processes using `uv run` for isolated execution +- **Process Architecture**: MCP tools spawn server processes using `uv run --prerelease=allow`/`uvx` for isolated execution + +The processor also uses a standard Fetch MCP server (installed/executed via `uvx mcp-server-fetch`) that is not implemented in this repository. ## Available MCP Servers -### 1. Azure Blob Storage Server (MCPBlobIOPlugin.py) +### 1. Azure Blob Storage Server (MCPBlobIOTool.py) **Service Name:** `azure_blob_io_service` Provides integration with Azure Blob Storage using FastMCP framework: **Capabilities:** + - Blob upload and download operations - Container management and listing - File metadata operations @@ -67,7 +71,7 @@ Provides integration with Azure Blob Storage using FastMCP framework: **Environment Configuration:** -The server supports multiple authentication methods through environment variables: +The server supports these authentication methods through environment variables: ```bash # Option 1: Azure Storage Account with DefaultAzureCredential (Recommended) @@ -75,27 +79,20 @@ STORAGE_ACCOUNT_NAME=your_storage_account_name # Option 2: Connection String (Alternative) AZURE_STORAGE_CONNECTION_STRING=DefaultEndpointsProtocol=https;AccountName=... - -# Option 3: Storage Account with Access Key (Not recommended for production) -STORAGE_ACCOUNT_NAME=your_storage_account_name -STORAGE_ACCOUNT_KEY=your_storage_account_key ``` **Authentication Methods:** -1. **DefaultAzureCredential** (Recommended for production): - - Uses managed identity in Azure environments - - Uses Azure CLI credentials for local development - - Requires `STORAGE_ACCOUNT_NAME` environment variable -2. **Connection String**: - - Requires `AZURE_STORAGE_CONNECTION_STRING` environment variable - - Contains embedded authentication information +1. **DefaultAzureCredential** (Recommended): + - Uses managed identity in Azure environments + - Uses Azure CLI credentials for local development + - Requires `STORAGE_ACCOUNT_NAME` -3. **Account Key**: - - Requires both `STORAGE_ACCOUNT_NAME` and `STORAGE_ACCOUNT_KEY` - - Not recommended for production environments +2. **Connection String** (Development alternative): + - Requires `AZURE_STORAGE_CONNECTION_STRING` **Available Tools:** + - `save_content_to_blob()`: Save content to Azure Blob Storage - `read_blob_content()`: Read blob content as text - `check_blob_exists()`: Verify blob existence with metadata @@ -107,14 +104,15 @@ STORAGE_ACCOUNT_KEY=your_storage_account_key - `copy_blob()`: Copy blobs within or across containers - `find_blobs()`: Search blobs using wildcard patterns -### 2. Microsoft Docs Server (MCPMicrosoftDocs.py) +### 2. Microsoft Learn Docs Server (HTTP) -**Service Name:** `microsoft_docs_service` +**Tool Name:** `Microsoft Learn MCP` Provides Microsoft documentation integration through HTTP-based MCP connection: GitHub Microsoft Docs MCP Server - [MicrosoftDocs/mcp](https://github.com/microsoftdocs/mcp) **Capabilities:** + - Microsoft Learn documentation access - Azure service documentation retrieval - Semantic search across Microsoft documentation @@ -127,22 +125,45 @@ GitHub Microsoft Docs MCP Server - [MicrosoftDocs/mcp](https://github.com/micros No environment variables required. Uses HTTP connection to Microsoft's public MCP server. **Connection Details:** + - **Protocol:** HTTP-based MCP connection - **URL:** `https://learn.microsoft.com/api/mcp` -- **Type:** MCPStreamableHttpPlugin -- **Requirements:** semantic-kernel with MCP support +- **Type:** `MCPStreamableHTTPTool` +- **Requirements:** Agent Framework MCP tool support **Available Tools:** + - `microsoft_docs_search()`: Semantic search against Microsoft documentation - `microsoft_docs_fetch()`: Fetch complete documentation pages in markdown format -### 3. Datetime Utilities Server (MCPDatetimePlugin.py) +### 3. Fetch Server (stdio) + +**Tool Name:** `Fetch MCP Tool` + +Provides generic URL fetch capabilities via a standard MCP server. + +**Capabilities:** + +- Fetch public HTTP(S) content when Microsoft Learn MCP is not sufficient +- Lightweight web retrieval for validation and cross-checking + +**Environment Configuration:** + +No environment variables required. + +**Runtime Requirements:** + +- `uvx` available in PATH +- Fetch server executable: `uvx mcp-server-fetch` + +### 4. Datetime Utilities Server (MCPDatetimeTool.py) **Service Name:** `datetime_service` Provides date and time operations using FastMCP framework: **Capabilities:** + - Current timestamp generation in multiple formats - Date and time parsing and formatting - Time zone conversions and handling @@ -156,15 +177,18 @@ Provides date and time operations using FastMCP framework: No environment variables required. Uses system time and optional timezone libraries. **Optional Dependencies:** + - **pytz**: Enhanced timezone support (recommended) - **zoneinfo**: Python 3.9+ timezone support (fallback) **Timezone Support:** + - Default timezone: UTC - Supported aliases: PT, ET, MT, CT, PST, PDT, EST, EDT, MST, MDT, CST, CDT - Full timezone names supported when pytz or zoneinfo available **Available Tools:** + - `get_current_timestamp()`: Get current timestamp in various formats - `format_datetime()`: Format datetime strings - `convert_timezone()`: Convert between timezones @@ -172,6 +196,56 @@ No environment variables required. Uses system time and optional timezone librar - `parse_datetime()`: Parse datetime strings - `get_relative_time()`: Calculate relative time descriptions +### 5. Mermaid Validation Server (MCPMermaidTool.py) + +**Service Name:** `mermaid_service` + +Provides Mermaid diagram validation and best-effort auto-fixing using FastMCP. + +**Capabilities:** + +- Validate Mermaid snippets generated during design documentation +- Best-effort normalization and fixing for common Mermaid formatting issues +- Validate/fix Mermaid blocks embedded in Markdown + +**Environment Configuration:** + +No environment variables required. + +**Available Tools:** + +- `validate_mermaid()`: Validate Mermaid code (heuristic) +- `fix_mermaid()`: Normalize and best-effort fix Mermaid code +- `validate_mermaid_in_markdown()`: Validate Mermaid blocks inside Markdown +- `fix_mermaid_in_markdown()`: Fix Mermaid blocks inside Markdown + +### 6. YAML Inventory Server (MCPYamlInventoryTool.py) + +**Service Name:** `yaml_inventory_service` + +Generates a deterministic inventory for converted Kubernetes YAML manifests and writes the inventory back to Azure Blob Storage. + +**Capabilities:** + +- Scan converted YAML/YML blobs under a given folder path +- Extract `apiVersion`, `kind`, `metadata.name`, `metadata.namespace` +- Group resources into a suggested apply order (deterministic) +- Write a `converted_yaml_inventory.json` artifact to Blob Storage + +**Environment Configuration:** + +Uses the same Azure Blob Storage environment variables as the Blob server: + +```bash +STORAGE_ACCOUNT_NAME=your_storage_account_name +# or +AZURE_STORAGE_CONNECTION_STRING=DefaultEndpointsProtocol=https;AccountName=... +``` + +**Available Tools:** + +- `generate_converted_yaml_inventory()`: Generate and write an inventory JSON for YAML blobs in blob storage + ## Complete MCP Configuration Setup ### Environment Setup Example @@ -187,7 +261,9 @@ export STORAGE_ACCOUNT_NAME="migrationstorageacct" # No additional environment variables needed for: # - DateTime Server (system time) -# - Microsoft Docs Server (HTTP connection) +# - Microsoft Learn MCP (HTTP connection) +# - Fetch MCP Tool (stdio) +# - Mermaid Server (stdio) ``` ### Integration in Migration Process @@ -196,40 +272,46 @@ The MCP servers integrate into the migration workflow as follows: 1. **Analysis Phase**: - `azure_blob_io_service`: Read source Kubernetes configurations - - `microsoft_docs_service`: Research Azure best practices + - `Microsoft Learn MCP`: Research Azure best practices + - `Fetch MCP Tool`: Fetch supporting references as needed - `datetime_service`: Timestamp analysis reports 2. **Design Phase**: - `azure_blob_io_service`: Save architecture designs - - `microsoft_docs_service`: Validate Azure service capabilities + - `Microsoft Learn MCP`: Validate Azure service capabilities + - `Fetch MCP Tool`: Fetch supporting references as needed - `datetime_service`: Track design timestamps + - `mermaid_service`: Validate/fix Mermaid diagrams in design outputs 3. **Conversion Phase**: - `azure_blob_io_service`: Save converted YAML configurations - `azure_blob_io_service`: Generate configuration comparisons + - `Fetch MCP Tool`: Fetch supporting references as needed - `datetime_service`: Track conversion timestamps 4. **Documentation Phase**: - `azure_blob_io_service`: Save migration reports - `azure_blob_io_service`: Generate migration documentation + - `yaml_inventory_service`: Generate converted YAML inventory for runbooks + - `Fetch MCP Tool`: Fetch supporting references as needed - `datetime_service`: Create migration timeline ### Agent-to-MCP Mapping Each expert agent uses specific MCP servers: -| Agent | Primary MCP Servers | Use Cases | -| ----------------------- | -------------------- | --------------------------------------------------- | -| **Technical Architect** | blob, docs, datetime | Architecture analysis, best practices research | -| **Azure Expert** | blob, docs, datetime | Azure-specific optimizations, service documentation | -| **EKS/GKE Expert** | blob, docs, datetime | Source platform analysis, migration patterns | -| **YAML Expert** | blob, docs, datetime | Configuration conversion, YAML validation | -| **QA Engineer** | blob, docs, datetime | Quality assurance, testing validation | -| **Technical Writer** | blob, docs, datetime | Documentation generation, report creation | +| Agent | MCP Tools Available | Use Cases | +| --------------------------------------------------------------- | ------------------------------------------------------ | ------------------------------------------------------------ | +| **Technical Architect** | docs, fetch, blob, datetime | Architecture analysis, best practices research | +| **Azure Architect** | docs, fetch, blob, datetime | Azure-specific optimizations, service documentation | +| **Platform Expert (EKS/GKE/OpenShift/Rancher/Tanzu/OnPremK8s)** | docs, fetch, blob, datetime | Source platform analysis, migration patterns | +| **YAML Expert** | docs, fetch, blob, datetime | Configuration conversion, YAML validation | +| **QA Engineer** | docs, fetch, blob, datetime | Quality assurance, testing validation | +| **Technical Writer** | docs, fetch, blob, datetime, yaml-inventory (doc step) | Documentation generation, runbook artifacts, report creation | -## Creating Custom MCP Servers with Semantic Kernel +## Creating Custom MCP Servers (FastMCP + Agent Framework tools) -The solution uses Semantic Kernel's MCP connectors to integrate with MCP servers. There are two main patterns for adding custom MCP servers: +The processor integrates MCP servers as **Agent Framework tools**. There are two main patterns for adding custom MCP servers: ### Pattern 1: Stdio-based MCP Servers (Local Processes) @@ -240,7 +322,7 @@ This pattern is used for local MCP servers that run as separate processes (like Create a FastMCP server implementation: ```python -# src/plugins/mcp_server/mcp_custom_service/mcp_custom_service.py +# src/processor/src/libs/mcp_server/custom_service/mcp_custom_service.py from fastmcp import FastMCP @@ -277,83 +359,63 @@ if __name__ == "__main__": mcp.run() ``` -#### Step 2: Create the Semantic Kernel Plugin +#### Step 2: Create an Agent Framework tool wrapper -Create a client plugin that connects to your MCP server: +Create a wrapper that exposes your FastMCP server as an Agent Framework tool: ```python -# src/plugins/mcp_server/MCPCustomServicePlugin.py +# src/processor/src/libs/mcp_server/MCPCustomServiceTool.py import os from pathlib import Path -def get_custom_service_plugin(): - """ - Create an MCP plugin for Custom Service Operations. - Cross-platform compatible for Windows, Linux, and macOS. - - Returns: - MCPStdioPlugin: Configured Custom Service MCP plugin - - Raises: - RuntimeError: If MCP setup validation fails - """ - try: - # Lazy import to avoid hanging during module import - from semantic_kernel.connectors.mcp import MCPStdioPlugin - - return MCPStdioPlugin( - name="custom_service", - description="MCP plugin for Custom Service Operations", - command="uv", - args=[ - f"--directory={str(Path(os.path.dirname(__file__)).joinpath('mcp_custom_service'))}", - "run", - "mcp_custom_service.py", - ], - env=dict(os.environ), # Pass environment variables if needed - ) - except ImportError as e: - print(f"MCP support not available: {e}") - return None +from agent_framework import MCPStdioTool + +def get_custom_service_mcp() -> MCPStdioTool: + """Create and return a stdio MCP tool for the custom FastMCP server.""" + + server_dir = Path(__file__).parent / "custom_service" + return MCPStdioTool( + name="custom_service", + command="uv", + args=[ + f"--directory={server_dir}", + "run", + "mcp_custom_service.py", + ], + env=dict(os.environ), + ) ``` ### Pattern 2: HTTP-based MCP Servers (Remote Services) This pattern is used for remote MCP servers accessible via HTTP (like the Microsoft Docs server). -#### Step 1: Create the HTTP Plugin +#### Step 1: Create the HTTP tool wrapper ```python -# src/plugins/mcp_server/MCPRemoteServicePlugin.py +# src/processor/src/libs/mcp_server/MCPRemoteService.py -try: - from semantic_kernel.connectors.mcp import MCPStreamableHttpPlugin - MCP_AVAILABLE = True -except ImportError: - MCP_AVAILABLE = False - MCPStreamableHttpPlugin = None +from agent_framework import MCPStreamableHTTPTool -def get_remote_service_plugin(): +def get_remote_service_mcp() -> MCPStreamableHTTPTool: """ - Create an MCP Streamable HTTP Plugin for remote service access. + Create an MCP Streamable HTTP tool for remote service access. Available tools: - remote_search: Search remote service - remote_fetch: Fetch data from remote service Returns: - MCPStreamableHttpPlugin: Configured plugin for remote MCP Server, or None if MCP not available + MCPStreamableHTTPTool: Configured tool for the remote MCP server """ - if not MCP_AVAILABLE or MCPStreamableHttpPlugin is None: - return None - - return MCPStreamableHttpPlugin( + return MCPStreamableHTTPTool( name="remote_service", description="Access Remote Service", url="https://your-remote-service.com/api/mcp", ) ``` + ## Troubleshooting ### Common Issues and Solutions @@ -361,10 +423,12 @@ def get_remote_service_plugin(): #### 1. Azure Blob Storage Authentication Issues **Symptoms:** + - `[FAILED] AZURE STORAGE AUTHENTICATION FAILED` messages - Agents unable to save or read blob content **Solutions:** + ```bash # Check environment variables echo $STORAGE_ACCOUNT_NAME @@ -379,6 +443,7 @@ az storage blob list --account-name $STORAGE_ACCOUNT_NAME --container-name defau ``` **Authentication Checklist:** + - ✅ `STORAGE_ACCOUNT_NAME` environment variable set - ✅ Azure CLI authenticated (`az login`) - ✅ Storage account exists and accessible @@ -387,17 +452,19 @@ az storage blob list --account-name $STORAGE_ACCOUNT_NAME --container-name defau #### 2. MCP Server Process Issues **Symptoms:** + - Timeout errors when calling MCP tools - Server not responding to tool calls **Solutions:** + ```bash # Check if UV is available uv --version # Test MCP server directly -cd src/plugins/mcp_server/mcp_blob_io_operation -uv run mcp_blob_io_operation.py +cd src/processor/src/libs/mcp_server/blob_io_operation +uv run --prerelease=allow mcp_blob_io_operation.py # Check Python environment python --version @@ -405,6 +472,7 @@ which python ``` **Process Checklist:** + - ✅ UV package manager installed - ✅ Python 3.12+ available - ✅ Virtual environment activated @@ -413,22 +481,25 @@ which python #### 3. Microsoft Docs Server Connection Issues **Symptoms:** + - Documentation search returns no results - HTTP connection timeouts **Solutions:** + ```bash # Test HTTP connectivity curl -I https://learn.microsoft.com/api/mcp -# Check semantic-kernel MCP support -python -c "from semantic_kernel.connectors.mcp import MCPStreamableHttpPlugin; print('MCP support available')" +# Check Agent Framework MCP support +python -c "from agent_framework import MCPStreamableHTTPTool; print('MCP support available')" ``` **Connection Checklist:** + - ✅ Internet connectivity available - ✅ No firewall blocking HTTP requests -- ✅ Semantic Kernel with MCP support installed +- ✅ Agent Framework dependencies installed (see [src/processor/pyproject.toml](../src/processor/pyproject.toml)) For additional information, refer to: diff --git a/docs/CustomizeExpertAgents.md b/docs/CustomizeExpertAgents.md index 48026b1..69b9ebd 100644 --- a/docs/CustomizeExpertAgents.md +++ b/docs/CustomizeExpertAgents.md @@ -4,7 +4,7 @@ This guide explains how to add custom expert agents to the Container Migration S ## Overview -The solution uses a multi-agent orchestration pattern where specialized expert agents collaborate through Semantic Kernel GroupChat patterns. You can add custom agents to support additional cloud platforms, specialized workloads, or domain-specific expertise. +The solution uses a multi-agent orchestration pattern where specialized expert agents collaborate through Agent Framework orchestrations (including group chat). You can add custom agents to support additional cloud platforms, specialized workloads, or domain-specific expertise. ## Current Expert Agent Architecture @@ -13,279 +13,84 @@ The solution uses a multi-agent orchestration pattern where specialized expert a The solution includes these expert agents: - **Technical Architect**: Overall architecture analysis and design decisions -- **Azure Expert**: Azure-specific optimizations and Well-Architected Framework compliance -- **GKE Expert**: Google Kubernetes Engine specific knowledge and migration patterns +- **Azure Architect / AKS Expert**: Azure-specific optimizations and Well-Architected Framework compliance +- **GKE Expert**: Google Kubernetes Engine (GKE/Anthos) specific knowledge and migration patterns - **EKS Expert**: Amazon Elastic Kubernetes Service expertise and AWS-to-Azure translations +- **OpenShift Expert**: Red Hat OpenShift specific knowledge and migration patterns +- **Rancher Expert**: Rancher/RKE/RKE2/K3s specific knowledge and migration patterns +- **Tanzu Expert**: VMware Tanzu/TKG specific knowledge and migration patterns +- **OnPremK8s Expert**: Self-managed/on-prem Kubernetes migration patterns - **QA Engineer**: Validation, testing strategies, and quality assurance - **YAML Expert**: Configuration transformation and syntax optimization ### Agent Structure -Each expert agent consists of: -- **Agent Info File**: Defines agent metadata and capabilities (`agent_info.py`) -- **Prompt Files**: Specialized prompts for different phases - - `prompt-analysis.txt`: Analysis phase prompts - - `prompt-design.txt`: Design phase prompts - - `prompt-documentation.txt`: Documentation phase prompts - - `prompt-yaml.txt`: YAML conversion phase prompts +In the current processor implementation, “expert agents” are configured primarily through: -## Adding a New Expert Agent +- **Prompt files** under each step’s `agents/` folder +- **Registry/config** (analysis only) to select platform experts dynamically +- **Step orchestrators** that construct `AgentInfo` objects and run group chat -### Step 1: Create Agent Directory +## Adding a New Expert Agent -Create a new directory under `src/agents/` for your custom agent: +### Step 1: Add prompt file(s) -```bash -mkdir src/agents/your_custom_expert -``` +Add your expert prompt file to the step(s) it should participate in: -### Step 2: Create Agent Info File +- Analysis: [src/processor/src/steps/analysis/agents/](../src/processor/src/steps/analysis/agents/) +- Design: [src/processor/src/steps/design/agents/](../src/processor/src/steps/design/agents/) +- YAML conversion: [src/processor/src/steps/convert/agents/](../src/processor/src/steps/convert/agents/) +- Documentation: [src/processor/src/steps/documentation/agents/](../src/processor/src/steps/documentation/agents/) -Create `src/agents/your_custom_expert/agent_info.py` following the existing pattern: - -```python -from agents.agent_info_util import AgentInfo - -def get_agent_info() -> AgentInfo: - return AgentInfo( - name="YourCustomExpert", - description="Expert in your specialized domain with deep knowledge of platform-specific patterns and migration strategies", - instructions=""" - You are a specialized expert in [YOUR DOMAIN]. Your role is to: - - 1. **Domain Analysis**: Analyze configurations specific to your platform/domain - 2. **Migration Patterns**: Identify platform-specific migration challenges and solutions - 3. **Best Practices**: Apply domain-specific best practices and optimizations - 4. **Integration Guidance**: Provide guidance on integrating with Azure services - - **Key Responsibilities:** - - Identify domain-specific configuration patterns - - Recommend migration strategies and transformations - - Validate configurations against domain best practices - - Provide expert insights for documentation - - **Communication Style:** - - Be specific and technical in your analysis - - Reference domain-specific documentation and patterns - - Provide actionable recommendations - - Collaborate effectively with other expert agents - """, - agent_name="YourCustomExpert", - agent_instructions_token_count=200 # Approximate token count - ) -``` +Use the existing prompt files in those folders as templates. -### Step 3: Create Specialized Prompts +### Step 2: Register the expert -#### Analysis Phase Prompt -Create `src/agents/your_custom_expert/prompt-analysis.txt`: +Analysis experts are loaded dynamically from: -``` -# Your Custom Expert - Analysis Phase - -You are a specialized expert in [YOUR DOMAIN] with deep knowledge of platform-specific configuration patterns, migration challenges, and Azure integration strategies. - -## Your Role in Analysis Phase - -**Primary Objectives:** -1. **Domain Detection**: Identify configurations specific to your platform/domain -2. **Complexity Assessment**: Evaluate migration complexity for your domain -3. **Pattern Recognition**: Identify domain-specific patterns and dependencies -4. **Initial Recommendations**: Provide preliminary migration guidance - -**Analysis Focus Areas:** -- Platform-specific configuration patterns -- Domain-specific networking, storage, or compute requirements -- Integration points and dependencies -- Security and compliance considerations -- Performance and scalability factors - -**Expected Deliverables:** -- Domain-specific configuration analysis -- Migration complexity assessment -- Preliminary transformation recommendations -- Integration considerations for Azure - -**Collaboration Guidelines:** -- Work closely with Technical Architect for overall strategy -- Coordinate with Azure Expert for Azure-specific optimizations -- Support QA Engineer with domain-specific validation requirements -``` +- [src/processor/src/steps/analysis/orchestration/platform_registry.json](../src/processor/src/steps/analysis/orchestration/platform_registry.json) -#### Design Phase Prompt -Create `src/agents/your_custom_expert/prompt-design.txt`: +Add your expert there to have it participate in the analysis phase. -``` -# Your Custom Expert - Design Phase - -You are responsible for transforming domain-specific configurations to Azure-optimized architectures following Azure Well-Architected Framework principles. - -## Your Role in Design Phase - -**Primary Objectives:** -1. **Architecture Transformation**: Design Azure-native architectures for your domain -2. **Service Mapping**: Map domain-specific services to Azure equivalents -3. **Optimization Strategy**: Apply Azure optimizations for your domain -4. **Integration Design**: Design integration patterns with Azure services - -**Design Focus Areas:** -- Azure service selection and configuration -- Network architecture and connectivity patterns -- Storage and data management strategies -- Security and identity integration -- Monitoring and observability design - -**Azure Well-Architected Principles:** -- **Reliability**: Design for high availability and disaster recovery -- **Security**: Implement defense-in-depth security strategies -- **Cost Optimization**: Optimize resource utilization and costs -- **Operational Excellence**: Design for monitoring and automation -- **Performance Efficiency**: Optimize for performance and scalability - -**Expected Deliverables:** -- Detailed Azure architecture design -- Service mapping and configuration recommendations -- Integration patterns and connectivity design -- Cost optimization recommendations -``` +For other phases, add an `AgentInfo(...)` entry in the relevant step orchestrator’s `prepare_agent_infos()` implementation. -#### YAML Conversion Phase Prompt -Create `src/agents/your_custom_expert/prompt-yaml.txt`: +### Step 4: Register the Agent -``` -# Your Custom Expert - YAML Conversion Phase - -You are responsible for converting domain-specific configurations to Azure Kubernetes Service (AKS) compatible YAML with platform-specific optimizations. - -## Your Role in YAML Conversion - -**Primary Objectives:** -1. **Configuration Transformation**: Convert domain configs to AKS-compatible YAML -2. **Azure Integration**: Integrate with Azure services (Key Vault, Monitor, etc.) -3. **Security Hardening**: Apply Azure security best practices -4. **Optimization**: Optimize for Azure performance and cost - -**Conversion Focus Areas:** -- Workload Identity integration for secure service access -- Azure Key Vault integration for secrets management -- Azure Monitor integration for observability -- Network policies and security contexts -- Resource quotas and limits optimization -- Storage class mapping to Azure disk types - -**Azure-Specific Transformations:** -- Convert service accounts to Workload Identity -- Map persistent volumes to Azure disk storage classes -- Transform ingress to Azure Application Gateway or nginx -- Convert monitoring to Azure Monitor/Prometheus -- Apply Azure security policies and contexts - -**Expected Deliverables:** -- Fully converted AKS-compatible YAML files -- Azure service integration configurations -- Security and networking optimizations -- Performance and cost optimization recommendations -``` +Add your agent to the appropriate step orchestrator so it participates in the group-chat collaboration for that phase. -#### Documentation Phase Prompt -Create `src/agents/your_custom_expert/prompt-documentation.txt`: +#### Update Step Orchestrators -``` -# Your Custom Expert - Documentation Phase - -You are responsible for creating comprehensive documentation for domain-specific migration decisions, transformations, and recommendations. - -## Your Role in Documentation - -**Primary Objectives:** -1. **Migration Documentation**: Document all domain-specific transformation decisions -2. **Expert Insights**: Provide detailed analysis and recommendations -3. **Implementation Guidance**: Create actionable implementation instructions -4. **Best Practices**: Document domain-specific best practices for Azure - -**Documentation Focus Areas:** -- Domain-specific migration challenges and solutions -- Azure service integration patterns and configurations -- Security and compliance considerations -- Performance optimization recommendations -- Operational guidance and monitoring strategies - -**Documentation Structure:** -- **Executive Summary**: High-level migration overview and recommendations -- **Technical Analysis**: Detailed technical assessment and transformation decisions -- **Implementation Guide**: Step-by-step implementation instructions -- **Best Practices**: Domain-specific Azure best practices -- **Troubleshooting**: Common issues and resolution strategies - -**Expected Deliverables:** -- Comprehensive migration documentation -- Implementation guides and runbooks -- Best practices and recommendations -- Technical decision rationale and justifications -``` +The processor uses step-level orchestrators under: -### Step 4: Register the Agent +- [src/processor/src/steps/analysis/orchestration/](../src/processor/src/steps/analysis/orchestration/) +- [src/processor/src/steps/design/orchestration/](../src/processor/src/steps/design/orchestration/) +- [src/processor/src/steps/convert/orchestration/](../src/processor/src/steps/convert/orchestration/) +- [src/processor/src/steps/documentation/orchestration/](../src/processor/src/steps/documentation/orchestration/) -Add your agent to the orchestration configuration. You need to modify the orchestrator files to include your new agent. +Each orchestrator builds its agent set using `AgentInfo` objects and runs a `GroupChatOrchestrator`. -**Note**: The actual implementation uses the existing orchestration pattern. Here's how to add your agent: +**Analysis phase (platform experts)** is registry-driven via: -#### Import Your Agent +- [src/processor/src/steps/analysis/orchestration/platform_registry.json](../src/processor/src/steps/analysis/orchestration/platform_registry.json) -Add the import statement in the orchestrator file where you want to include your agent: +To add a new analysis expert: -```python -# Add this import alongside existing agent imports -from agents.your_custom_expert.agent_info import get_agent_info as your_custom_expert -``` +1. Add a new prompt file under [src/processor/src/steps/analysis/agents/](../src/processor/src/steps/analysis/agents/) +2. Add an entry to `platform_registry.json` pointing at the prompt file and desired `agent_name` -#### Update Orchestrator Methods +For other phases, add a new `AgentInfo(...)` entry in the relevant orchestrator’s `prepare_agent_infos()` implementation. -Based on the actual implementation in `src/libs/steps/orchestration/`, add your agent to the appropriate `_create_*_agents` methods: +Minimal example (pattern used in the codebase): -**Analysis Orchestrator** (`src/libs/steps/orchestration/analysis_orchestration.py`): ```python -async def _create_analysis_agents( - self, mcp_context, process_context, agent_response_callback=None, telemetry=None -) -> GroupChatOrchestration: - """Helper method to create analysis agents with task-local MCP context.""" - agents = [] - - # Chief Architect - leads analysis - architect_config = architect_agent(phase=MigrationPhase.ANALYSIS).render(**self.process_context) - agent_architect = await mcp_context.create_agent(architect_config) - agents.append(agent_architect) - - # Platform experts for source detection - eks_config = eks_expert(phase=MigrationPhase.ANALYSIS).render(**self.process_context) - agent_eks = await mcp_context.create_agent(eks_config) - agents.append(agent_eks) - - gke_config = gke_expert(phase=MigrationPhase.ANALYSIS).render(**self.process_context) - agent_gke = await mcp_context.create_agent(gke_config) - agents.append(agent_gke) - - # Add your custom expert - custom_config = your_custom_expert(phase=MigrationPhase.ANALYSIS).render(**self.process_context) - agent_custom = await mcp_context.create_agent(custom_config) - agents.append(agent_custom) - - orchestration = GroupChatOrchestration( - members=agents, - manager=AnalysisStepGroupChatManager( - step_name="Analysis", - step_objective="Discover source files and identify platform type", - service=self.kernel_agent.kernel.services["default"], - max_rounds=50, - process_context=self.process_context, - telemetry=telemetry, - ), - agent_response_callback=agent_response_callback, - ) - - return orchestration +from libs.agent_framework.agent_info import AgentInfo + +expert_info = AgentInfo( + agent_name="YourCustomExpert", + agent_instruction=instruction_text, + tools=self.mcp_tools, +) ``` -**Design Orchestrator**: Follow the same pattern for Design and other orchestrators based on your requirements and the existing implementation patterns in `src/libs/steps/orchestration/design_orchestration.py`. Your implementation uses **phase-specific agent selection**, meaning you can include your agent in specific phases only: @@ -336,7 +141,7 @@ The actual implementation supports conditional agent inclusion. Study the existi - YAML phase includes transformation specialists - Documentation phase involves technical writers -Refer to the actual orchestration implementations in `src/libs/steps/orchestration/` for patterns. +Refer to the actual orchestration implementations in `src/processor/src/steps/**/orchestration/` for patterns (under [src/processor/src/steps/](../src/processor/src/steps/)). ## Troubleshooting @@ -356,14 +161,11 @@ Refer to the actual orchestration implementations in `src/libs/steps/orchestrati ## Examples -Study the existing expert agent implementations in your codebase for real patterns: +Study the existing expert prompts and orchestrators in your codebase for real patterns: -- `src/agents/azure_expert/agent_info.py` - Azure service expertise -- `src/agents/eks_expert/agent_info.py` - EKS platform knowledge -- `src/agents/gke_expert/agent_info.py` - GKE platform expertise -- `src/agents/technical_architect/agent_info.py` - Architecture oversight -- `src/agents/qa_engineer/agent_info.py` - Quality assurance patterns -- `src/agents/yaml_expert/agent_info.py` - Configuration transformation +- Prompt files: `src/processor/src/steps/**/agents/` (under [src/processor/src/steps/](../src/processor/src/steps/)) +- Analysis expert registry: [src/processor/src/steps/analysis/orchestration/platform_registry.json](../src/processor/src/steps/analysis/orchestration/platform_registry.json) +- Orchestrators: `src/processor/src/steps/**/orchestration/` (under [src/processor/src/steps/](../src/processor/src/steps/)) These provide tested patterns for implementing custom expert agents in your migration solution. @@ -371,12 +173,12 @@ These provide tested patterns for implementing custom expert agents in your migr 1. **Review Existing Agents**: Study the existing agent implementations for patterns 2. **Plan Your Agent**: Define the specific expertise and responsibilities -3. **Implement Step by Step**: Start with agent info, then add prompts and integration +3. **Implement Step by Step**: Start with a prompt file, then add registry/orchestrator integration 4. **Test Thoroughly**: Validate the agent works well in the full orchestration flow 5. **Document Your Agent**: Create documentation for future maintenance and extension For additional help with custom agent development, refer to: - [Multi-Agent Orchestration Approach](MultiAgentOrchestration.md) -- [Process Framework Implementation](ProcessFrameworkGuide.md) +- [Processor Workflow Implementation](ProcessFrameworkGuide.md) - [Technical Architecture](TechnicalArchitecture.md) diff --git a/docs/CustomizeMigrationPrompts.md b/docs/CustomizeMigrationPrompts.md index f60af7d..d846a9e 100644 --- a/docs/CustomizeMigrationPrompts.md +++ b/docs/CustomizeMigrationPrompts.md @@ -4,7 +4,7 @@ This guide explains how to customize the migration prompts used by expert agents ## Overview -The Container Migration Solution Accelerator uses a sophisticated prompt engineering system with phase-specific prompts for each expert agent. You can customize these prompts to: +The Container Migration Solution Accelerator uses a step-based prompt engineering system with per-agent prompts plus per-step orchestration templates (task + coordinator). You can customize these prompts to: - Adapt to specific organizational standards and requirements - Support additional platforms or technologies @@ -13,26 +13,32 @@ The Container Migration Solution Accelerator uses a sophisticated prompt enginee ## Prompt Architecture -### Phase-Based Prompt System +### Step-Based Prompt System (v2) -Each migration phase uses specialized prompts: +Prompts are organized per step (analysis/design/convert/documentation). Each step uses **three** prompt layers: -1. **Analysis Phase**: Platform detection, configuration analysis, and complexity assessment -2. **Design Phase**: Azure architecture design and service mapping -3. **YAML Conversion Phase**: Configuration transformation and Azure integration -4. **Documentation Phase**: Migration documentation and implementation guidance +1. **Task prompt template** (`prompt_task.txt`): the main step instruction given to the orchestrator. +2. **Agent instruction prompts** (`agents/prompt_*.txt`): role/persona prompts for each participating agent. +3. **Coordinator routing prompt** (`prompt_coordinator.txt`): enforces sequencing, sign-off rules, and termination criteria. -### Agent-Specific Prompts +In addition, some steps use a **platform registry** (`platform_registry.json`) to select platform experts dynamically. -Each expert agent has its own prompt files: +The canonical layout looks like: +```text +src/processor/src/steps// +├── agents/ +│ ├── prompt_*.txt # Agent instruction prompts +│ └── ... +└── orchestration/ + ├── prompt_task.txt # Step task template (rendered with variables) + ├── prompt_coordinator.txt # Coordinator routing/sign-off rules + └── platform_registry.json # (analysis/design/documentation only) ``` -src/agents/{agent_name}/ -├── prompt-analysis.txt # Analysis phase prompt -├── prompt-design.txt # Design phase prompt -├── prompt-yaml.txt # YAML conversion prompt -└── prompt-documentation.txt # Documentation prompt -``` + +### Template Variables + +The step task template is rendered at runtime using `{{variable}}` placeholders (for example `{{process_id}}`, `{{container_name}}`, `{{source_file_folder}}`, `{{output_file_folder}}`, `{{workspace_file_folder}}`). ## Customizing Existing Prompts @@ -40,14 +46,26 @@ src/agents/{agent_name}/ Locate the prompt files you want to customize: +- Analysis step prompts: [src/processor/src/steps/analysis/](../src/processor/src/steps/analysis/) +- Design step prompts: [src/processor/src/steps/design/](../src/processor/src/steps/design/) +- Convert step prompts: [src/processor/src/steps/convert/](../src/processor/src/steps/convert/) +- Documentation step prompts: [src/processor/src/steps/documentation/](../src/processor/src/steps/documentation/) + +Note: prompt filenames can differ by step (for example, the AKS agent prompt is [src/processor/src/steps/analysis/agents/prompt_aks.txt](../src/processor/src/steps/analysis/agents/prompt_aks.txt) in analysis, but [src/processor/src/steps/convert/agents/prompt_aks_expert.txt](../src/processor/src/steps/convert/agents/prompt_aks_expert.txt) in convert). + ```bash -# List all prompt files -find src/agents -name "prompt-*.txt" +# List all agent prompt files +find src/processor/src/steps -path "*/agents/*" -name "prompt*.txt" + +# List orchestration templates (task + coordinator) +find src/processor/src/steps -path "*/orchestration/*" -name "prompt_*.txt" -o -name "prompt_task.txt" -o -name "prompt_coordinator.txt" # Example output: -# src/agents/azure_expert/prompt-analysis.txt -# src/agents/eks_expert/prompt-design.txt -# src/agents/yaml_expert/prompt-yaml.txt +# src/processor/src/steps/analysis/agents/prompt_architect.txt +# src/processor/src/steps/convert/agents/prompt_yaml_expert.txt +# src/processor/src/steps/documentation/agents/prompt_technical_writer.txt +# src/processor/src/steps/analysis/orchestration/prompt_task.txt +# src/processor/src/steps/analysis/orchestration/prompt_coordinator.txt ``` ### Step 2: Backup Original Prompts @@ -56,10 +74,14 @@ Create backups before customization: ```bash # Create backup directory -mkdir src/agents/backups +mkdir src/processor/src/steps/backups # Backup specific prompts -cp src/agents/azure_expert/prompt-analysis.txt src/agents/backups/ +cp src/processor/src/steps/analysis/agents/prompt_architect.txt src/processor/src/steps/backups/ + +# (Recommended) also backup orchestration templates you plan to change +cp src/processor/src/steps/analysis/orchestration/prompt_task.txt src/processor/src/steps/backups/ +cp src/processor/src/steps/analysis/orchestration/prompt_coordinator.txt src/processor/src/steps/backups/ ``` ### Step 3: Customize Prompt Content @@ -67,9 +89,11 @@ cp src/agents/azure_expert/prompt-analysis.txt src/agents/backups/ Edit the prompt files to include your customizations: ```text -# Example: Customizing Azure Expert Analysis Prompt +# Example: Customizing AKS Expert prompt (Analysis step) + +# File: src/processor/src/steps/analysis/agents/prompt_aks.txt -# Azure Expert - Analysis Phase (CUSTOMIZED FOR ORGANIZATION) +# AKS Expert - Analysis Step (CUSTOMIZED FOR ORGANIZATION) You are an Azure solution architect with expertise in enterprise migrations and deep knowledge of Azure Well-Architected Framework principles. @@ -79,7 +103,7 @@ You are an Azure solution architect with expertise in enterprise migrations and - **Cost Optimization**: Target 30% cost reduction from current platform - **Naming Convention**: Follow company naming standards (env-app-region-001) -## Your Role in Analysis Phase +## Your Role in the Analysis Step **Primary Objectives:** 1. **Platform Assessment**: Evaluate source platform compatibility with Azure @@ -90,6 +114,38 @@ You are an Azure solution architect with expertise in enterprise migrations and [Continue with existing prompt content...] ``` +### Step 3b: Customize the Step Task Template (if needed) + +The step task template is often the highest-leverage place to customize deliverables, report structure, sign-off format, and hard-termination rules. + +- Analysis task template: [src/processor/src/steps/analysis/orchestration/prompt_task.txt](../src/processor/src/steps/analysis/orchestration/prompt_task.txt) +- Design task template: [src/processor/src/steps/design/orchestration/prompt_task.txt](../src/processor/src/steps/design/orchestration/prompt_task.txt) +- Convert task template: [src/processor/src/steps/convert/orchestration/prompt_task.txt](../src/processor/src/steps/convert/orchestration/prompt_task.txt) +- Documentation task template: [src/processor/src/steps/documentation/orchestration/prompt_task.txt](../src/processor/src/steps/documentation/orchestration/prompt_task.txt) + +If you edit these files, keep the `{{...}}` placeholders intact unless you are also updating the orchestrator/runtime to provide new values. + +### Step 3c: Customize Coordinator Routing Rules (advanced) + +The coordinator prompt controls routing and completion rules (for example sign-off gating and hard termination). Customize with care: + +- Analysis coordinator rules: [src/processor/src/steps/analysis/orchestration/prompt_coordinator.txt](../src/processor/src/steps/analysis/orchestration/prompt_coordinator.txt) +- Design coordinator rules: [src/processor/src/steps/design/orchestration/prompt_coordinator.txt](../src/processor/src/steps/design/orchestration/prompt_coordinator.txt) +- Convert coordinator rules: [src/processor/src/steps/convert/orchestration/prompt_coordinator.txt](../src/processor/src/steps/convert/orchestration/prompt_coordinator.txt) +- Documentation coordinator rules: [src/processor/src/steps/documentation/orchestration/prompt_coordinator.txt](../src/processor/src/steps/documentation/orchestration/prompt_coordinator.txt) + +Small wording changes here can change when the workflow terminates or which agents get selected. + +### Step 3d: Customize Platform Expert Selection (registry-driven steps) + +For steps that load platform experts from a registry, update the registry to add/remove experts or adjust selection signals: + +- Analysis registry: [src/processor/src/steps/analysis/orchestration/platform_registry.json](../src/processor/src/steps/analysis/orchestration/platform_registry.json) +- Design registry: [src/processor/src/steps/design/orchestration/platform_registry.json](../src/processor/src/steps/design/orchestration/platform_registry.json) +- Documentation registry: [src/processor/src/steps/documentation/orchestration/platform_registry.json](../src/processor/src/steps/documentation/orchestration/platform_registry.json) + +Each entry points to an agent prompt file under that step’s `agents/` folder. + ### Step 4: Add Organization-Specific Context Include your organization's specific requirements: @@ -203,6 +259,7 @@ Include your organization's specific requirements: - Application deployment automation - Configuration drift detection and remediation ``` + ## Best Practices for Prompt Customization ### 1. Maintain Consistency @@ -241,6 +298,7 @@ Include your organization's specific requirements: **Problem**: Prompt exceeds token limits **Solution**: + - Break down complex prompts into sections - Use prompt templates with dynamic insertion - Prioritize most important requirements @@ -249,6 +307,7 @@ Include your organization's specific requirements: **Problem**: Agent responses vary significantly **Solution**: + - Add more specific constraints and examples - Use structured output formats - Implement response validation @@ -257,6 +316,7 @@ Include your organization's specific requirements: **Problem**: Agent lacks sufficient context for decisions **Solution**: + - Enhance prompts with more background information - Add examples of expected inputs and outputs - Include decision criteria and constraints @@ -319,6 +379,7 @@ Include your organization's specific requirements: 5. **Monitor and Iterate**: Continuously monitor and improve prompt performance For additional information, refer to: + - [Adding Custom Expert Agents](CustomizeExpertAgents.md) - [Multi-Agent Orchestration Approach](MultiAgentOrchestration.md) - [Technical Architecture](TechnicalArchitecture.md) diff --git a/docs/CustomizingAzdParameters.md b/docs/CustomizingAzdParameters.md index 7989a84..2477615 100644 --- a/docs/CustomizingAzdParameters.md +++ b/docs/CustomizingAzdParameters.md @@ -6,20 +6,20 @@ By default this template will use the environment name as the prefix to prevent ## Parameters -| Name | Type | Example Value | Purpose | -| ------------------------------- | ------- | ----------------------- | ------------------------------------------------------------------------------------- | -| `AZURE_ENV_NAME` | string | `conmig` | Sets the environment name prefix for all Azure resources. | -| `AZURE_LOCATION` | string | `westus` | Sets the location/region for all Azure resources. | -| `AZURE_SECONDARY_LOCATION` | string | `eastus2` | Specifies a secondary Azure region. | -| `AZURE_CONTAINER_REGISTRY_HOST` | string | `myregistry.azurecr.io` | Specifies the container registry from which to pull app container images. | -| `AZURE_AI_DEPLOYMENT_LOCATION` | string | `eastus2` | Specifies alternative location for AI model resources. | -| `AZURE_AI_DEPLOYMENT_TYPE` | string | `GlobalStandard` | Defines the model deployment type (allowed values: `Standard`, `GlobalStandard`). | -| `AZURE_AI_MODEL_NAME` | string | `o3` | Specifies the `o` model name. | -| `AZURE_AI_MODEL_VERSION` | string | `2025-04-16` | Specifies the `o` model version. | -| `AZURE_AI_MODEL_CAPACITY` | integer | `200` | Sets the model capacity (choose based on your subscription's available `o` capacity). | -| `AZURE_ENV_VM_ADMIN_USERNAME` | string | `` | The administrator username for the virtual machine. | -| `AZURE_ENV_VM_ADMIN_PASSWORD` | string | `` | The administrator password for the virtual machine. | -| `AZURE_ENV_IMAGETAG` | string | `latest` | Specifies the container image tag to use for deployment. | +| Name | Type | Example Value | Purpose | +| ------------------------------- | ------- | ----------------------- | ---------------------------------------------------------------------------------- | +| `AZURE_ENV_NAME` | string | `conmig` | Sets the environment name prefix for all Azure resources. | +| `AZURE_LOCATION` | string | `westus` | Sets the location/region for all Azure resources. | +| `AZURE_SECONDARY_LOCATION` | string | `eastus2` | Specifies a secondary Azure region. | +| `AZURE_CONTAINER_REGISTRY_HOST` | string | `myregistry.azurecr.io` | Specifies the container registry from which to pull app container images. | +| `AZURE_AI_DEPLOYMENT_LOCATION` | string | `eastus2` | Specifies alternative location for AI model resources. | +| `AZURE_AI_DEPLOYMENT_TYPE` | string | `GlobalStandard` | Defines the model deployment type (allowed values: `Standard`, `GlobalStandard`). | +| `AZURE_AI_MODEL_NAME` | string | `gpt-5.1` | Specifies the Azure OpenAI model name to deploy. | +| `AZURE_AI_MODEL_VERSION` | string | `your-model-version` | Specifies the model version (use a version available in your region/subscription). | +| `AZURE_AI_MODEL_CAPACITY` | integer | `200` | Sets the model capacity (choose based on your subscription's available quota). | +| `AZURE_ENV_VM_ADMIN_USERNAME` | string | `` | The administrator username for the virtual machine. | +| `AZURE_ENV_VM_ADMIN_PASSWORD` | string | `` | The administrator password for the virtual machine. | +| `AZURE_ENV_IMAGETAG` | string | `latest` | Specifies the container image tag to use for deployment. | ## How to Set a Parameter diff --git a/docs/DeleteResourceGroup.md b/docs/DeleteResourceGroup.md index 3869ee5..d0cb761 100644 --- a/docs/DeleteResourceGroup.md +++ b/docs/DeleteResourceGroup.md @@ -41,7 +41,7 @@ If you don’t want to delete the entire resource group, follow these steps: 1. Open **Azure Portal** and go to the **Resource groups** section. 2. Click on the specific **resource group**. -3. Select the **resource** you want to delete (e.g., App Service, Storage Account). +3. Select the **resource** you want to delete (e.g., Container App, Storage Account). 4. Click **Delete** at the top. ![Delete Individual Resource](./images/portal_web_app_delete.png) diff --git a/docs/DeploymentGuide.md b/docs/DeploymentGuide.md index 77afe40..d1dc01b 100644 --- a/docs/DeploymentGuide.md +++ b/docs/DeploymentGuide.md @@ -12,12 +12,12 @@ This guide walks you through deploying the Container Migration Solution Accelera Ensure you have access to an [Azure subscription](https://azure.microsoft.com/free/) with the following permissions: -| **Required Permission/Role** | **Scope** | **Purpose** | -|------------------------------|-----------|-------------| -| **Contributor** | Subscription level | Create and manage Azure resources | -| **User Access Administrator** | Subscription level | Manage user access and role assignments | -| **Role Based Access Control** | Subscription/Resource Group level | Configure RBAC permissions | -| **App Registration Creation** | Azure Active Directory | Create and configure authentication | +| **Required Permission/Role** | **Scope** | **Purpose** | +| ----------------------------- | --------------------------------- | --------------------------------------- | +| **Contributor** | Subscription level | Create and manage Azure resources | +| **User Access Administrator** | Subscription level | Manage user access and role assignments | +| **Role Based Access Control** | Subscription/Resource Group level | Configure RBAC permissions | +| **App Registration Creation** | Azure Active Directory | Create and configure authentication | **🔍 How to Check Your Permissions:** @@ -51,7 +51,7 @@ Ensure you have access to an [Azure subscription](https://azure.microsoft.com/fr - [Azure App Configuration](https://learn.microsoft.com/en-us/azure/azure-app-configuration/) - [Azure Cosmos DB](https://learn.microsoft.com/en-us/azure/cosmos-db/) - [Azure Queue Storage](https://learn.microsoft.com/en-us/azure/storage/queues/) -- [o3 Model Capacity](https://learn.microsoft.com/en-us/azure/ai-foundry/foundry-models/concepts/models-sold-directly-by-azure) +- [GPT-5.1 Model Capacity](https://learn.microsoft.com/en-us/azure/ai-foundry/foundry-models/concepts/models-sold-directly-by-azure) **Recommended Regions:** East US, East US2, Australia East, UK South, France Central @@ -77,12 +77,12 @@ Select one of the following options to deploy the Container Migration Solution A ### Environment Comparison -| **Option** | **Best For** | **Prerequisites** | **Setup Time** | -|------------|--------------|-------------------|----------------| -| **GitHub Codespaces** | Quick deployment, no local setup required | GitHub account | ~3-5 minutes | -| **VS Code Dev Containers** | Fast deployment with local tools | Docker Desktop, VS Code | ~5-10 minutes | -| **VS Code Web** | Quick deployment, no local setup required | Azure account | ~2-4 minutes | -| **Local Environment** | Enterprise environments, full control | All tools individually | ~15-30 minutes | +| **Option** | **Best For** | **Prerequisites** | **Setup Time** | +| -------------------------- | ----------------------------------------- | ----------------------- | -------------- | +| **GitHub Codespaces** | Quick deployment, no local setup required | GitHub account | ~3-5 minutes | +| **VS Code Dev Containers** | Fast deployment with local tools | Docker Desktop, VS Code | ~5-10 minutes | +| **VS Code Web** | Quick deployment, no local setup required | Azure account | ~2-4 minutes | +| **Local Environment** | Enterprise environments, full control | All tools individually | ~15-30 minutes | **💡 Recommendation:** For fastest deployment, start with **GitHub Codespaces** - no local installation required. @@ -174,14 +174,14 @@ Review the configuration options below. You can customize any settings that meet ### 3.1 Choose Deployment Type (Optional) -| **Aspect** | **Development/Testing (Default)** | **Production** | -|------------|-----------------------------------|----------------| -| **Configuration File** | `main.parameters.json` (sandbox) | Copy `main.waf.parameters.json` to `main.parameters.json` | -| **Security Controls** | Minimal (for rapid iteration) | Enhanced (production best practices) | -| **Cost** | Lower costs | Cost optimized | -| **Use Case** | POCs, development, testing | Production workloads | -| **Framework** | Basic configuration | [Well-Architected Framework](https://learn.microsoft.com/en-us/azure/well-architected/) | -| **Features** | Core functionality | Reliability, security, operational excellence | +| **Aspect** | **Development/Testing (Default)** | **Production** | +| ---------------------- | --------------------------------- | --------------------------------------------------------------------------------------- | +| **Configuration File** | `main.parameters.json` (sandbox) | Copy `main.waf.parameters.json` to `main.parameters.json` | +| **Security Controls** | Minimal (for rapid iteration) | Enhanced (production best practices) | +| **Cost** | Lower costs | Cost optimized | +| **Use Case** | POCs, development, testing | Production workloads | +| **Framework** | Basic configuration | [Well-Architected Framework](https://learn.microsoft.com/en-us/azure/well-architected/) | +| **Features** | Core functionality | Reliability, security, operational excellence | **To use production configuration:** @@ -272,7 +272,7 @@ azd up **During deployment, you'll be prompted for:** 1. **Environment name** (e.g., "conmig") - Must be 3-16 characters long, alphanumeric only 2. **Azure subscription** selection -3. **Azure AI Foundry deployment region** - Select a region with available o3 model quota for AI operations +3. **Azure AI Foundry deployment region** - Select a region with available GPT-5.1 model quota for AI operations 4. **Primary location** - Select the region where your infrastructure resources will be deployed 5. **Resource group** selection (create new or use existing) @@ -310,7 +310,7 @@ After successful deployment: Follow the detailed workflow to test the migration functionality: **Quick Test Steps:** -1. Download sample YAML files from the [`/data`](../data/) folder (EKS or GKE samples) +1. Download sample YAML files from the [`/data`](../data/) folder (EKS or GKE samples). You can also upload manifests from other supported source platforms (e.g., OpenShift/Rancher/Tanzu/on-prem Kubernetes). 2. Upload the files to the application 3. Click **Start Processing** to begin the migration 4. Monitor the batch processing status (typically takes 20-30 minutes) diff --git a/docs/ExtendPlatformSupport.md b/docs/ExtendPlatformSupport.md index fff85c1..6ff6e1c 100644 --- a/docs/ExtendPlatformSupport.md +++ b/docs/ExtendPlatformSupport.md @@ -1,6 +1,6 @@ # Extending Platform Support -This guide explains how to extend the Container Migration Solution Accelerator to support additional source platforms beyond EKS and GKE, and provides comprehensive setup instructions for different development environments including Windows, Linux, and macOS. +This guide explains how to extend the Container Migration Solution Accelerator to support additional source Kubernetes platforms/distributions (including enterprise and on-prem/self-managed environments), and provides comprehensive setup instructions for different development environments including Windows, Linux, and macOS. ## Overview @@ -22,8 +22,12 @@ The solution is designed with a modular architecture that makes it relatively st The solution currently supports: - **Amazon EKS**: Full migration support with AWS-specific service mapping -- **Google GKE**: Complete GKE to AKS transformation capabilities -- **Generic Kubernetes**: Basic Kubernetes workload migration +- **Google GKE/Anthos**: GKE/Anthos to AKS transformation capabilities +- **Red Hat OpenShift**: OpenShift-aware analysis and migration guidance +- **Rancher (RKE/RKE2/K3s)**: Rancher-aware analysis and migration guidance +- **VMware Tanzu (TKG/TKGS)**: Tanzu-aware analysis and migration guidance +- **Self-managed / On-prem Kubernetes**: On-prem-aware analysis and migration guidance +- **Generic Kubernetes**: Baseline Kubernetes workload migration ### Platform Detection System @@ -74,55 +78,36 @@ To illustrate the complete process, let's walk through adding Red Hat OpenShift #### Example Agent Creation (Step 2) ```bash -# Create OpenShift expert directory -mkdir src/agents/openshift_expert - -# Create agent files -touch src/agents/openshift_expert/agent_info.py -touch src/agents/openshift_expert/prompt-analysis.txt -touch src/agents/openshift_expert/prompt-design.txt -touch src/agents/openshift_expert/prompt-yaml.txt -touch src/agents/openshift_expert/prompt-documentation.txt -``` +# Add an OpenShift expert prompt for the analysis step +touch src/processor/src/steps/analysis/agents/prompt_openshift.txt -```python -# src/agents/openshift_expert/agent_info.py -from agents.agent_info_util import MigrationPhase, load_prompt_text -from utils.agent_builder import AgentType, agent_info - -def get_agent_info(phase: MigrationPhase | str | None = None) -> agent_info: - """Get OpenShift Expert agent info with optional phase-specific prompt.""" - return agent_info( - agent_name="OpenShift_Expert", - agent_type=AgentType.ChatCompletionAgent, - agent_description="Red Hat OpenShift expert specializing in container platform migration to Azure Kubernetes Service with deep knowledge of Routes, DeploymentConfigs, ImageStreams, and OpenShift operators.", - agent_instruction=load_prompt_text(phase=phase), - ) +# Register it so it can be selected during analysis +edit src/processor/src/steps/analysis/orchestration/platform_registry.json ``` +For other phases, add corresponding prompt files under: + +- [src/processor/src/steps/design/agents/](../src/processor/src/steps/design/agents/) +- [src/processor/src/steps/convert/agents/](../src/processor/src/steps/convert/agents/) +- [src/processor/src/steps/documentation/agents/](../src/processor/src/steps/documentation/agents/) + #### How Platform Detection Really Works Your codebase uses **intelligent multi-agent conversation** for platform detection, not explicit detection classes. Here's how it actually works: ```python # Real platform detection flow (from analysis_orchestration.py) -# 1. Multi-agent team collaborates: Technical Architect + EKS Expert + GKE Expert +# 1. Multi-agent team collaborates: Technical Architect + one or more platform experts (registry-driven) # 2. Agents examine YAML files and discuss findings through conversation # 3. Expert consensus emerges through collaborative analysis # 4. Result captured in termination_output.platform_detected -# Current agent team structure (analysis_orchestration.py): -from agents.technical_architect.agent_info import get_agent_info as architect_agent -from agents.eks_expert.agent_info import get_agent_info as eks_expert -from agents.gke_expert.agent_info import get_agent_info as gke_expert - -# Agent creation with phase-specific prompts: -architect_config = architect_agent(phase=MigrationPhase.ANALYSIS) -eks_config = eks_expert(phase=MigrationPhase.ANALYSIS) -gke_config = gke_expert(phase=MigrationPhase.ANALYSIS) +# Current agent team structure (analysis_orchestrator.py): +# - Technical Architect (orchestrates analysis) +# - Platform experts loaded from `platform_registry.json` (e.g., EKS, GKE/Anthos, OpenShift, Rancher, Tanzu, OnPremK8s) # Result structure: -platform_detected: str = Field(description="Platform detected (EKS or GKE only)") +platform_detected: str = Field(description="Platform detected (e.g., EKS/GKE/OpenShift/Rancher/Tanzu/OnPremK8s)") confidence_score: str = Field(description="Confidence score for platform detection (e.g., '95%')") ``` @@ -131,21 +116,19 @@ confidence_score: str = Field(description="Confidence score for platform detecti To add OpenShift support, you would register the new expert in the analysis orchestration: ```python -# In analysis_orchestration.py, add import: -from agents.openshift_expert.agent_info import get_agent_info as openshift_expert - -# In _create_analysis_agents method, add OpenShift expert: -openshift_config = openshift_expert(phase=MigrationPhase.ANALYSIS).render( - **self.process_context -) -agent_openshift = await mcp_context.create_agent(openshift_config) -agents.append(agent_openshift) +# Add an OpenShift expert prompt file under: +# src/processor/src/steps/analysis/agents/ +# +# Then add a registry entry under: +# src/processor/src/steps/analysis/orchestration/platform_registry.json +# +# The analysis orchestrator loads experts from the registry and constructs AgentInfo +# participants with MCP tools, then runs the group chat orchestration. # The multi-agent conversation will then include: # - Technical Architect (orchestrates analysis) -# - EKS Expert (recognizes AWS/EKS patterns) -# - GKE Expert (recognizes GCP/GKE patterns) -# - OpenShift Expert (recognizes OpenShift-specific patterns) +# - One or more platform experts selected via registry signals +# (e.g., EKS, GKE/Anthos, OpenShift, Rancher, Tanzu, OnPremK8s) ``` ### Step-by-Step Implementation Guide @@ -187,124 +170,61 @@ Before adding support, analyze the target platform: Create a specialized expert agent for the new platform: ```bash -# Create agent directory -mkdir src/agents/platform_name_expert - -# Create required files -touch src/agents/platform_name_expert/agent_info.py -touch src/agents/platform_name_expert/prompt-analysis.txt -touch src/agents/platform_name_expert/prompt-design.txt -touch src/agents/platform_name_expert/prompt-yaml.txt -touch src/agents/platform_name_expert/prompt-documentation.txt -``` - -Example agent structure based on existing codebase: +# Add a new prompt file for your platform expert (analysis step) +touch src/processor/src/steps/analysis/agents/prompt__expert.txt -```python -# src/agents/new_platform_expert/agent_info.py - -from agents.agent_info_util import MigrationPhase, load_prompt_text -from utils.agent_builder import AgentType, agent_info - -def get_agent_info(phase: MigrationPhase | str | None = None) -> agent_info: - """Get New Platform Expert agent info with optional phase-specific prompt. - - Args: - phase (MigrationPhase | str | None): Migration phase ('analysis', 'design', 'yaml', 'documentation'). - If provided, loads phase-specific prompt. - """ - return agent_info( - agent_name="NewPlatform_Expert", - agent_type=AgentType.ChatCompletionAgent, - agent_description="Platform expert specializing in [Platform Name] with expertise in Kubernetes migration initiatives.", - agent_instruction=load_prompt_text(phase=phase), - ) - -# Note: Create prompt files in the same directory: -# - prompt-analysis.txt -# - prompt-design.txt -# - prompt-yaml.txt -# - prompt-documentation.txt +# Register the expert for analysis selection +edit src/processor/src/steps/analysis/orchestration/platform_registry.json ``` +Create or customize the prompt file content to cover detection signals, key resources, migration challenges, and Azure mapping guidance. + ### Step 3: Integrate with Existing Orchestration Add your new platform expert to the existing orchestration logic: ```python # Integration with existing analysis orchestration -# Reference: src/libs/steps/orchestration/analysis_orchestration.py - -# When adding platform detection, integrate with the existing -# analysis orchestration structure that includes: -# - Technical Architect (chief architect) -# - EKS Expert -# - GKE Expert -# - Your new platform expert - -# Follow the existing pattern of phase-specific agent loading -# that uses MigrationPhase enum values +# Reference: src/processor/src/steps/analysis/orchestration/analysis_orchestrator.py +# Platform experts for analysis are configured via: +# src/processor/src/steps/analysis/orchestration/platform_registry.json ``` -**Note:** The current codebase uses a sophisticated orchestration system with `GroupChatOrchestration` and phase-specific prompts. Platform detection logic should be integrated with the existing analysis orchestration rather than creating new standalone classes. +**Note:** Platform detection should be integrated into the existing analysis step orchestration rather than creating a new standalone pipeline. ### Step 4: Update Agent Registration When adding new platform support, ensure proper agent registration in the orchestration system: -```python -# Follow the existing pattern in analysis_orchestration.py -# which imports agents like: -from agents.eks_expert.agent_info import get_agent_info as eks_expert -from agents.gke_expert.agent_info import get_agent_info as gke_expert -from agents.technical_architect.agent_info import get_agent_info as architect_agent - -# Add your new platform expert: -from agents.your_platform_expert.agent_info import get_agent_info as your_platform_expert -``` +For the analysis phase, register your new platform expert by: + +1. Adding a new prompt file under [src/processor/src/steps/analysis/agents/](../src/processor/src/steps/analysis/agents/) +2. Adding an entry to [src/processor/src/steps/analysis/orchestration/platform_registry.json](../src/processor/src/steps/analysis/orchestration/platform_registry.json) + +For other phases, update the relevant step orchestrator’s `prepare_agent_infos()` to include a new `AgentInfo`. -**Note:** The current codebase follows the Semantic Kernel Process Framework with specialized orchestration for each migration phase. Platform-specific logic should integrate with the existing `StepGroupChatOrchestrator` and `GroupChatOrchestration` patterns. +**Note:** The current codebase uses an Agent Framework workflow with step-level group-chat orchestration. Platform-specific logic should integrate with the existing step orchestrators (analysis/design/yaml/documentation) and their group-chat patterns, rather than introducing a new end-to-end pipeline. ### Step 5: Update the Analysis Orchestration Integrate your new platform expert into the actual analysis orchestration: ```python -# In src/libs/steps/orchestration/analysis_orchestration.py -# Add import for your new expert: -from agents.openshift_expert.agent_info import get_agent_info as openshift_expert - -# In the _create_analysis_agents method, add your expert to the agent team: -async def _create_analysis_agents(self, mcp_context, process_context, agent_response_callback=None, telemetry=None): - agents = [] - - # Technical Architect (orchestrates the analysis) - architect_config = architect_agent(phase=MigrationPhase.ANALYSIS).render(**self.process_context) - agent_architect = await mcp_context.create_agent(architect_config) - agents.append(agent_architect) - - # Platform experts for source detection - eks_config = eks_expert(phase=MigrationPhase.ANALYSIS).render(**self.process_context) - agent_eks = await mcp_context.create_agent(eks_config) - agents.append(agent_eks) - - gke_config = gke_expert(phase=MigrationPhase.ANALYSIS).render(**self.process_context) - agent_gke = await mcp_context.create_agent(gke_config) - agents.append(agent_gke) - - # Add your new platform expert - openshift_config = openshift_expert(phase=MigrationPhase.ANALYSIS).render(**self.process_context) - agent_openshift = await mcp_context.create_agent(openshift_config) - agents.append(agent_openshift) - - return GroupChatOrchestration(members=agents, manager=AnalysisStepGroupChatManager(...)) +# In src/processor/src/steps/analysis/orchestration/analysis_orchestrator.py +# Platform experts are loaded from platform_registry.json. +# Add a new registry entry pointing to your prompt file, e.g.: +# { +# "agent_name": "OpenShift Expert", +# "prompt_file": "prompt-openshift-expert.txt" +# } ``` **Key Points:** -- Each expert gets phase-specific prompts through `MigrationPhase.ANALYSIS` -- Agents are created with the MCP context for tool access -- The `render(**self.process_context)` provides runtime context to agents + +- Analysis experts are loaded from [src/processor/src/steps/analysis/orchestration/platform_registry.json](../src/processor/src/steps/analysis/orchestration/platform_registry.json) (config-driven) +- Each agent gets MCP tool access via the step orchestrator’s `self.mcp_tools` +- Prompts are rendered from files under the step’s `agents/` directory ### Step 6: Implement Platform-Specific Prompts @@ -390,46 +310,28 @@ Transform OpenShift workloads to Azure-native architectures following Azure Well Test your new platform expert using the existing testing patterns: ```python -# tests/unit/test_openshift_expert.py - -import pytest -from agents.openshift_expert.agent_info import get_agent_info -from agents.agent_info_util import MigrationPhase - -class TestOpenShiftExpert: - """Test OpenShift expert agent following existing patterns""" - - def test_agent_info_structure(self): - """Test that agent info follows the standard structure""" - agent_info = get_agent_info() - - # Verify required attributes exist - assert hasattr(agent_info, 'agent_name') - assert hasattr(agent_info, 'agent_type') - assert hasattr(agent_info, 'agent_description') - assert hasattr(agent_info, 'agent_instruction') - - # Verify agent name matches expected pattern - assert agent_info.agent_name == "OpenShift_Expert" - - def test_phase_specific_prompts(self): - """Test that phase-specific prompts are loaded correctly""" - # Test each migration phase - for phase in MigrationPhase: - agent_info = get_agent_info(phase=phase) - assert agent_info.agent_instruction is not None - assert len(agent_info.agent_instruction) > 0 - - def test_analysis_phase_prompt(self): - """Test analysis phase specific functionality""" - agent_info = get_agent_info(phase=MigrationPhase.ANALYSIS) - - # Verify the prompt contains OpenShift-specific content - prompt = agent_info.agent_instruction.lower() - assert "openshift" in prompt or "route" in prompt - +# src/processor/src/tests/unit/test_platform_registry.py + +import json +from pathlib import Path + + +def test_platform_registry_entry_exists(): + registry_path = Path("src/processor/src/steps/analysis/orchestration/platform_registry.json") + data = json.loads(registry_path.read_text(encoding="utf-8")) + + # Example: ensure an OpenShift expert is registered + assert any("openshift" in (item.get("agent_name", "").lower()) for item in data) + + +def test_platform_prompt_file_exists(): + prompt_path = Path("src/processor/src/steps/analysis/agents/prompt_openshift.txt") + assert prompt_path.exists() + assert "openshift" in prompt_path.read_text(encoding="utf-8").lower() + + # Run tests using existing test framework: -# uv run python -m pytest tests/unit/test_openshift_expert.py -v +# uv run --prerelease=allow python -m pytest src/processor/src/tests/unit -v ``` ## Troubleshooting Platform Extensions diff --git a/docs/LocalDevelopmentSetup.md b/docs/LocalDevelopmentSetup.md index e479870..0d36143 100644 --- a/docs/LocalDevelopmentSetup.md +++ b/docs/LocalDevelopmentSetup.md @@ -62,9 +62,9 @@ cd path/to/Container-Migration-Solution-Accelerator This project uses separate `.env` files in each service directory with different configuration requirements: -- **Processor**: `src/processor/src/.env` - Azure App Configuration URL -- **Backend API**: `src/backend-api/src/app/.env` - Azure App Configuration URL -- **Frontend**: `src/frontend/.env` - Azure AD authentication settings +- **Processor**: `.env` under [src/processor/src/](../src/processor/src/) - Azure App Configuration URL +- **Backend API**: `.env` under [src/backend-api/src/app/](../src/backend-api/src/app/) - Azure App Configuration URL +- **Frontend**: `.env` under [src/frontend/](../src/frontend/) - Azure AD authentication settings When copying `.env` samples, always navigate to the specific service directory first. @@ -256,7 +256,7 @@ cd src/processor ### 4.2. Configure Processor Environment Variables -Create a `.env` file in the `src/processor/src` directory (NOT in `src/processor` root): +Create a `.env` file in the [src/processor/src/](../src/processor/src/) directory (NOT in [src/processor/](../src/processor/) root): ```bash cd src @@ -272,7 +272,7 @@ Add the following to the `.env` file: APP_CONFIGURATION_URL=https://[Your app configuration service name].azconfig.io ``` -> **⚠️ Important**: The `.env` file must be located in `src/processor/src/` directory, not in `src/processor/` root. The application looks for the `.env` file in the same directory as `main.py` and `main_service.py`. +> **⚠️ Important**: The `.env` file must be located in [src/processor/src/](../src/processor/src/) directory, not in [src/processor/](../src/processor/) root. The application looks for the `.env` file in the same directory as `main.py` and `main_service.py`. ### 4.3. Install Processor Dependencies @@ -286,7 +286,7 @@ source .venv/bin/activate # Linux/WSL2 .\.venv\Scripts\Activate.ps1 # Windows PowerShell # Install dependencies -uv sync --python 3.12 +uv sync --python 3.12 --prerelease=allow ``` **Windows users**: If you encounter issues with the `uv` command not being found, use the Python Launcher instead: @@ -296,10 +296,10 @@ uv sync --python 3.12 py -3.12 -m uv venv .venv # Install dependencies -py -3.12 -m uv sync +py -3.12 -m uv sync --prerelease=allow ``` -> **⚠️ Important**: Always run `uv sync` (or `py -3.12 -m uv sync` on Windows) after creating the virtual environment to install all required dependencies. Missing dependencies will cause runtime errors like `ModuleNotFoundError: No module named 'pydantic'` or DNS resolution failures. +> **⚠️ Important**: This repo currently depends on a prerelease/dev version of Microsoft Agent Framework. Always run `uv sync --prerelease=allow` (or `py -3.12 -m uv sync --prerelease=allow` on Windows) after creating the virtual environment to install all required dependencies. Missing dependencies will cause runtime errors like `ModuleNotFoundError: No module named 'pydantic'` or DNS resolution failures. ### 4.4. Run the Processor @@ -363,7 +363,7 @@ cd src/backend-api ### 5.2. Configure Backend API Environment Variables -Create a `.env` file in the `src/backend-api/src/app` directory: +Create a `.env` file in the [src/backend-api/src/app/](../src/backend-api/src/app/) directory: ```bash cd src/app @@ -391,7 +391,7 @@ source .venv/bin/activate # Linux/WSL2 .\.venv\Scripts\Activate.ps1 # Windows PowerShell # Install dependencies -uv sync --python 3.12 +uv sync --python 3.12 --prerelease=allow ``` ### 5.4. Run the Backend API @@ -412,7 +412,7 @@ The Backend API will start at: > **📋 Terminal Reminder**: Open a **third dedicated terminal window (Terminal 3)** for the Frontend. Keep Terminals 1 (Processor) and 2 (Backend API) running. All commands assume you start from the **repository root directory**. -The UI is located under `src/frontend`. +The UI is located under [src/frontend/](../src/frontend/). ### 6.1. Navigate to Frontend Directory @@ -429,7 +429,7 @@ npm install ### 6.3. Configure UI Environment Variables -Create a `.env` file in the `src/frontend` directory: +Create a `.env` file in the [src/frontend/](../src/frontend/) directory: ```bash # Copy the example file @@ -538,7 +538,7 @@ uv venv .venv # Activate and reinstall source .venv/bin/activate # Linux # or .\.venv\Scripts\Activate.ps1 # Windows -uv sync --python 3.12 +uv sync --python 3.12 --prerelease=allow ``` #### Permission Issues (Linux) @@ -581,11 +581,11 @@ Before using the application, confirm all three services are running in separate ### Terminal Status Checklist -| Terminal | Service | Command | Expected Output | URL | -|----------|---------|---------|-----------------|-----| -| **Terminal 1** | Processor (Queue Mode) | `python -m main_service` | `INFO: No messages in main queue` (repeating every 5s) | N/A | -| **Terminal 2** | Backend API | `python -m uvicorn main:app --reload` | `INFO: Application startup complete` | http://localhost:8000 | -| **Terminal 3** | Frontend | `npm run dev` | `Local: http://localhost:5173/` | http://localhost:5173 | +| Terminal | Service | Command | Expected Output | URL | +| -------------- | ---------------------- | ------------------------------------- | ------------------------------------------------------ | --------------------- | +| **Terminal 1** | Processor (Queue Mode) | `python -m main_service` | `INFO: No messages in main queue` (repeating every 5s) | N/A | +| **Terminal 2** | Backend API | `python -m uvicorn main:app --reload` | `INFO: Application startup complete` | http://localhost:8000 | +| **Terminal 3** | Frontend | `npm run dev` | `Local: http://localhost:5173/` | http://localhost:5173 | ### Quick Verification @@ -625,7 +625,7 @@ Once all services are running (as confirmed in Step 7), you can: 1. **Access the Application**: Open `http://localhost:5173` in your browser to explore the frontend UI 2. **Try a Sample Workflow**: Follow [SampleWorkflow.md](SampleWorkflow.md) for a guided walkthrough of the migration process -3. **Explore the Codebase**: Start with `src/processor/src/main_service.py` to understand the agent architecture +3. **Explore the Codebase**: Start with [src/processor/src/main_service.py](../src/processor/src/main_service.py) to understand the agent architecture 4. **Customize Agents**: Follow [CustomizeExpertAgents.md](CustomizeExpertAgents.md) to modify agent behavior 5. **Extend Platform Support**: Follow [ExtendPlatformSupport.md](ExtendPlatformSupport.md) to add new cloud platforms diff --git a/docs/MCPServerGuide.md b/docs/MCPServerGuide.md index baaa6c7..c0d6527 100644 --- a/docs/MCPServerGuide.md +++ b/docs/MCPServerGuide.md @@ -4,7 +4,7 @@ This guide provides comprehensive information for implementing, configuring, and ## Overview -Model Context Protocol (MCP) servers provide a standardized way to extend AI agent capabilities with external tools, resources, and services. The Container Migration Solution Accelerator uses MCP to integrate with Azure services, file systems, Kubernetes clusters, and other external systems. +Model Context Protocol (MCP) servers provide a standardized way to extend AI agent capabilities with external tools, resources, and services. The Container Migration Solution Accelerator uses MCP to integrate with Azure services (for example: Blob Storage), Microsoft Learn documentation (HTTP), URL fetching, Mermaid validation, and YAML inventory generation. ## MCP Architecture @@ -14,7 +14,7 @@ Model Context Protocol (MCP) servers provide a standardized way to extend AI age graph TB subgraph "MCP Architecture" subgraph "Client Layer" - A[AI Agents
Semantic Kernel
GroupChat] + A[AI Agents
Agent Framework
Orchestrations] end subgraph "Protocol Layer" @@ -26,7 +26,7 @@ graph TB end subgraph "Integration Layer" - D[External Services
• Azure APIs
• File Systems
• Kubernetes
• Git Repositories] + D[External Services
• Azure APIs
• Blob Storage
• HTTP Resources
• Git Repositories] end A <--> B @@ -47,212 +47,83 @@ graph TB ```mermaid sequenceDiagram participant Agent as AI Agent - participant SK as Semantic Kernel + participant AF as Agent Framework participant MCP as MCP Server participant EXT as External Service - Agent->>SK: Request tool execution - SK->>MCP: JSON-RPC call + Agent->>AF: Request tool execution + AF->>MCP: JSON-RPC call MCP->>MCP: Validate request MCP->>EXT: Execute operation EXT-->>MCP: Return result MCP->>MCP: Process response - MCP-->>SK: JSON-RPC response - SK-->>Agent: Tool result + MCP-->>AF: JSON-RPC response + AF-->>Agent: Tool result ``` -## Base MCP Server Implementation - -### Abstract Base Server - -```python -from abc import ABC, abstractmethod -from typing import Dict, Any, List, Optional -import json -import asyncio - -class BaseMCPServer(ABC): - """Abstract base class for MCP servers""" - - def __init__(self, server_name: str, config: Dict[str, Any]): - self.server_name = server_name - self.config = config - self.tools = {} - self.resources = {} - self.prompts = {} - self.is_initialized = False - - @abstractmethod - async def initialize(self) -> bool: - """Initialize the MCP server""" - pass - - @abstractmethod - async def cleanup(self) -> None: - """Cleanup server resources""" - pass - - # Tool Management - async def list_tools(self) -> List[Dict[str, Any]]: - """List available tools""" - return [ - { - "name": name, - "description": tool.description, - "inputSchema": tool.input_schema - } - for name, tool in self.tools.items() - ] - - async def call_tool(self, name: str, arguments: Dict[str, Any]) -> Dict[str, Any]: - """Call a specific tool""" - if name not in self.tools: - raise ValueError(f"Tool '{name}' not found") - - tool = self.tools[name] - return await tool.execute(arguments) - - # Resource Management - async def list_resources(self) -> List[Dict[str, Any]]: - """List available resources""" - return [ - { - "uri": resource.uri, - "name": resource.name, - "description": resource.description, - "mimeType": resource.mime_type - } - for resource in self.resources.values() - ] - - async def read_resource(self, uri: str) -> Dict[str, Any]: - """Read a specific resource""" - if uri not in self.resources: - raise ValueError(f"Resource '{uri}' not found") - - resource = self.resources[uri] - return await resource.read() - - # Prompt Management - async def list_prompts(self) -> List[Dict[str, Any]]: - """List available prompts""" - return [ - { - "name": name, - "description": prompt.description, - "arguments": prompt.arguments - } - for name, prompt in self.prompts.items() - ] - - async def get_prompt(self, name: str, arguments: Dict[str, Any] = None) -> Dict[str, Any]: - """Get a specific prompt""" - if name not in self.prompts: - raise ValueError(f"Prompt '{name}' not found") - - prompt = self.prompts[name] - return await prompt.render(arguments or {}) -``` +## How MCP is implemented in this repo (v2) -### Tool Implementation - -```python -class MCPTool: - """Represents an MCP tool""" - - def __init__(self, name: str, description: str, input_schema: Dict[str, Any], - executor: callable): - self.name = name - self.description = description - self.input_schema = input_schema - self.executor = executor - - async def execute(self, arguments: Dict[str, Any]) -> Dict[str, Any]: - """Execute the tool with given arguments""" - try: - # Validate arguments against schema - self._validate_arguments(arguments) - - # Execute tool function - result = await self.executor(arguments) - - return { - "content": [ - { - "type": "text", - "text": str(result) - } - ] - } - - except Exception as e: - return { - "content": [ - { - "type": "text", - "text": f"Error executing tool {self.name}: {str(e)}" - } - ], - "isError": True - } - - def _validate_arguments(self, arguments: Dict[str, Any]): - """Validate arguments against input schema""" - # Implement JSON schema validation - pass -``` +In v2, MCP servers are exposed to agents as **Microsoft Agent Framework tools**: -### Resource Implementation - -```python -class MCPResource: - """Represents an MCP resource""" - - def __init__(self, uri: str, name: str, description: str, - mime_type: str, reader: callable): - self.uri = uri - self.name = name - self.description = description - self.mime_type = mime_type - self.reader = reader - - async def read(self) -> Dict[str, Any]: - """Read the resource content""" - try: - content = await self.reader() - - return { - "contents": [ - { - "uri": self.uri, - "mimeType": self.mime_type, - "text": content if isinstance(content, str) else json.dumps(content) - } - ] - } - - except Exception as e: - raise RuntimeError(f"Failed to read resource {self.uri}: {str(e)}") -``` +- **HTTP MCP tool**: used for remote MCP servers (Microsoft Learn) +- **Stdio MCP tool**: used for local MCP servers (spawned as subprocesses) + +Where to look: + +- Tool wrappers (Agent Framework tools): [src/processor/src/libs/mcp_server/](../src/processor/src/libs/mcp_server/) +- Local server implementations (FastMCP): `src/processor/src/libs/mcp_server/**/mcp_*.py` (under [src/processor/src/libs/mcp_server/](../src/processor/src/libs/mcp_server/)) +- Tool registration per step: `src/processor/src/steps/*/orchestration/*_orchestrator.py` (under [src/processor/src/steps/](../src/processor/src/steps/)) + +Notes: + +- Local servers in this repo are implemented using **FastMCP** and are started via `uv run --prerelease=allow ...`. +- The Fetch server is an external MCP server started via `uvx mcp-server-fetch`. +- The v2 orchestrators use the wrappers under [src/processor/src/libs/mcp_server/](../src/processor/src/libs/mcp_server/). + +## Current MCP tools/servers in v2 + +These are the MCP tools actually prepared by the v2 orchestrators: + +### Microsoft Learn (HTTP) + +- **Tool name**: `Microsoft Learn MCP` +- **Endpoint**: `https://learn.microsoft.com/api/mcp` +- **Used in**: Analysis, Design, Convert, Documentation +- **Wrapper**: [src/processor/src/libs/mcp_server/MCPMicrosoftDocs.py](../src/processor/src/libs/mcp_server/MCPMicrosoftDocs.py) + +### Fetch (stdio, external) + +- **Tool name**: `Fetch MCP Tool` +- **Executable**: `uvx mcp-server-fetch` +- **Used in**: Analysis, Design, Convert, Documentation + +### Azure Blob Storage (stdio) + +- **Tool name**: `azure_blob_io_service` +- **Used in**: Analysis, Design, Convert, Documentation +- **Wrapper**: [src/processor/src/libs/mcp_server/MCPBlobIOTool.py](../src/processor/src/libs/mcp_server/MCPBlobIOTool.py) +- **Server**: [src/processor/src/libs/mcp_server/blob_io_operation/mcp_blob_io_operation.py](../src/processor/src/libs/mcp_server/blob_io_operation/mcp_blob_io_operation.py) + +### Datetime utilities (stdio) -## Current MCP Server Implementation +- **Tool name**: `datetime_service` +- **Used in**: Analysis, Design, Convert, Documentation +- **Wrapper**: [src/processor/src/libs/mcp_server/MCPDatetimeTool.py](../src/processor/src/libs/mcp_server/MCPDatetimeTool.py) +- **Server**: [src/processor/src/libs/mcp_server/datetime/mcp_datetime.py](../src/processor/src/libs/mcp_server/datetime/mcp_datetime.py) -The Container Migration Solution Accelerator includes the following MCP servers: +### Mermaid validation/fix (stdio) -### MCPBlobIOPlugin.py -- **Purpose**: Azure Blob Storage operations -- **Capabilities**: Blob upload/download, container management, file operations -- **Usage**: Stores migration results, configuration backups, temporary files +- **Tool name**: `mermaid_service` +- **Used in**: Design +- **Wrapper**: [src/processor/src/libs/mcp_server/MCPMermaidTool.py](../src/processor/src/libs/mcp_server/MCPMermaidTool.py) +- **Server**: [src/processor/src/libs/mcp_server/mermaid/mcp_mermaid.py](../src/processor/src/libs/mcp_server/mermaid/mcp_mermaid.py) -### MCPMicrosoftDocs.py -- **Purpose**: Microsoft documentation integration -- **Capabilities**: Documentation search, content retrieval, reference lookup -- **Usage**: Access Azure documentation, best practices, configuration examples +### YAML inventory (stdio) -### MCPDatetimePlugin.py -- **Purpose**: Date and time utilities -- **Capabilities**: Timestamp generation, date formatting, duration calculations -- **Usage**: Migration tracking, log timestamping, scheduling operations +- **Tool name**: `yaml_inventory_service` +- **Used in**: Documentation +- **Wrapper**: [src/processor/src/libs/mcp_server/MCPYamlInventoryTool.py](../src/processor/src/libs/mcp_server/MCPYamlInventoryTool.py) +- **Server**: [src/processor/src/libs/mcp_server/yaml_inventory/mcp_yaml_inventory.py](../src/processor/src/libs/mcp_server/yaml_inventory/mcp_yaml_inventory.py) ## Best Practices diff --git a/docs/MultiAgentOrchestration.md b/docs/MultiAgentOrchestration.md index 0ec77ea..7b884f8 100644 --- a/docs/MultiAgentOrchestration.md +++ b/docs/MultiAgentOrchestration.md @@ -4,135 +4,64 @@ This guide provides an in-depth look at the multi-agent orchestration system use ## Overview -The Container Migration Solution Accelerator uses a sophisticated multi-agent orchestration approach built on Microsoft Semantic Kernel's GroupChatOrchestration functionality. This approach enables multiple specialized AI agents to collaborate effectively, bringing domain-specific expertise to different aspects of the migration process. +The Container Migration Solution Accelerator uses a multi-agent orchestration approach built on Microsoft Agent Framework group chat orchestration (`GroupChatOrchestrator`). This enables multiple specialized agents to collaborate per migration step (analysis/design/yaml/documentation), bringing domain expertise and quality gates to each phase. -## Core Principles +## Agent Roles -### 1. Specialization and Expertise +### Platform Experts (Source Kubernetes) -Each agent is designed with a specific area of expertise: +Platform experts provide source-platform-specific context and are selected dynamically based on platform signals detected during analysis (registry-driven). Built-in examples include **EKS**, **GKE/Anthos**, **OpenShift**, **Rancher (RKE/RKE2/K3s)**, **VMware Tanzu**, and **self-managed/on-prem Kubernetes**. -- **Domain Focus**: Agents specialize in specific platforms, technologies, or aspects of migration -- **Deep Knowledge**: Each agent has comprehensive prompts and training for their domain -- **Collaborative Intelligence**: Agents work together to solve complex problems -- **Quality Assurance**: Built-in validation and review processes +### EKS Expert -### 2. Structured Collaboration - -Agents collaborate through well-defined patterns: - -- **Phase-Based Orchestration**: Agents participate in specific migration phases -- **Consensus Building**: Multiple agents contribute to decisions -- **Conflict Resolution**: Structured approaches to handle disagreements -- **Quality Gates**: Validation checkpoints throughout the process - -### 3. Extensibility and Modularity - -The system is designed for easy extension: - -- **Plugin Architecture**: Easy addition of new agents and capabilities -- **Configurable Workflows**: Flexible orchestration patterns -- **Scalable Processing**: Efficient resource utilization -- **Platform Agnostic**: Support for multiple source and target platforms - -## Agent Architecture - -### Agent Specification - -Each agent is defined by: - -```python -class agent_info(BaseModel): - agent_name: str # Agent identifier - agent_type: AgentType # Agent type (ChatCompletionAgent, AzureAIAgent, etc.) - agent_system_prompt: str # System prompt for the agent - agent_instruction: str # Specific agent instructions - - def render(self, **context) -> 'agent_info': - """Render agent configuration with context variables""" - template = Template(self.agent_system_prompt) - rendered_prompt = template.render(**context) - - return agent_info( - agent_name=self.agent_name, - agent_type=self.agent_type, - agent_system_prompt=rendered_prompt, - agent_instruction=self.agent_instruction - ) -``` -### Agent Lifecycle - -Agents follow a structured lifecycle within the orchestration system: - -1. **Creation**: Agents are instantiated with specific configurations and context - - Agent Info Loading: Load agent-specific configuration and metadata - - Validation: Validate agent configuration and requirements - - Registration Orchestrator: Register agent with the orchestration manager - -2. **Initialization**: Agents receive their system prompts and establish MCP connections - - Kernel Setup: Initialize the Semantic Kernel environment - - Plugin Loading: Load and configure MCP plugins and tools - - Memory Setup: Establish agent memory and context management +- **Role**: Amazon EKS migration expertise +- **Responsibilities**: + - EKS workload analysis + - AWS service mapping (IAM/IRSA, ELB/ALB patterns, EBS/EFS) + - AWS-to-Azure translations -3. **Assignment**: Agents are assigned to specific phases and receive contextual information - - Phase Selection: Determine which migration phase the agent will participate in - - Context Setup: Configure agent with phase-specific context and data - - Prompt Loading: Load phase-specific system prompts and instructions +### GKE Expert -4. **Collaboration**: Agents participate in group chat orchestration and collaborative work - - Group Execution: Active participation in multi-agent group chat sessions - - Result Collection: Gather and consolidate outputs from collaborative sessions - - Quality Review: Validate and review outputs for quality and completeness - - Final Output: Generate final deliverables for the migration phase +- **Role**: Google GKE migration expertise +- **Responsibilities**: + - GKE workload analysis + - Google Cloud service mapping + - Container migration from GCR + - Network policy transformation + - Identity and access management -![Agent Lifecycle](images/readme/agent_lifecycle.png) +### OpenShift Expert -The lifecycle ensures proper resource management and clear handoffs between different migration phases. -### Agent Types +- **Role**: Red Hat OpenShift migration expertise +- **Responsibilities**: + - OpenShift resource detection (Routes, SCC, Operators) + - OpenShift-to-AKS mapping guidance -#### 1. Strategic Agents +### Rancher Expert -**Technical(Chief) Architect** -- **Role**: Overall migration strategy and architectural oversight +- **Role**: Rancher/RKE migration expertise - **Responsibilities**: - - Migration approach definition - - Architecture pattern selection - - Risk assessment and mitigation - - Technology stack recommendations - - Cross-functional coordination + - Rancher-managed cluster patterns (Fleet, Projects/RBAC) + - GitOps and multi-cluster management mapping + +### Tanzu Expert -**Azure Expert** -- **Role**: Azure platform expertise and optimization +- **Role**: VMware Tanzu/TKG migration expertise - **Responsibilities**: - - Azure service selection and configuration - - Well-Architected Framework compliance - - Cost optimization strategies - - Security pattern implementation - - Performance optimization + - Tanzu/TKG-specific identity and networking patterns + - Migration considerations for vSphere integration -#### 2. Platform-Specific Agents +### OnPremK8s Expert -**EKS Expert** -- **Role**: Amazon EKS migration expertise +- **Role**: Self-managed/on-prem Kubernetes migration expertise - **Responsibilities**: - - EKS configuration analysis - - AWS service mapping to Azure - - Container registry migration (ECR to ACR) - - Network configuration transformation - - IAM to Azure AD mapping + - Common on-prem dependencies (ingress/LB, storage, identity) + - On-prem-to-AKS modernization considerations -**GKE Expert** -- **Role**: Google GKE migration expertise -- **Responsibilities**: - - GKE workload analysis - - Google Cloud service mapping - - Container migration from GCR - - Network policy transformation - - Identity and access management +### 3. Quality and Documentation Agents -#### 3. Quality and Documentation Agents +#### QA Engineer -**QA Engineer** - **Role**: Quality assurance and validation - **Responsibilities**: - Migration plan validation @@ -141,7 +70,8 @@ The lifecycle ensures proper resource management and clear handoffs between diff - Risk identification - Compliance verification -**Technical Writer** +#### Technical Writer + - **Role**: Documentation quality and structure - **Responsibilities**: - Documentation organization @@ -150,9 +80,10 @@ The lifecycle ensures proper resource management and clear handoffs between diff - Process documentation - Knowledge base maintenance -#### 4. Specialized Technical Agents +### 4. Specialized Technical Agents + +#### YAML Expert -**YAML Expert** - **Role**: Configuration syntax and optimization - **Responsibilities**: - YAML syntax validation and optimization @@ -163,883 +94,67 @@ The lifecycle ensures proper resource management and clear handoffs between diff ## Orchestration Patterns -### 1. Step-Based Orchestration with MCP Context - -Each migration step uses specialized orchestration with TaskGroup-safe MCP context management: - -#### Analysis Step Orchestration - -```python -class AnalysisOrchestrator(StepGroupChatOrchestrator): - """Orchestrator specifically for Analysis step operations.""" +### 1. Step Orchestrators (Agent Framework) - async def create_analysis_orchestration_with_context( - self, mcp_context, process_context, agent_response_callback=None, telemetry=None - ) -> GroupChatOrchestration: - """Create analysis step orchestration with MCP context""" - - self.logger.info("[ART] Creating Analysis Step Group Chat Orchestration...") - - try: - # Create analysis-specific agents - orchestration = await self._create_analysis_agents( - mcp_context=mcp_context, - process_context=process_context, - agent_response_callback=agent_response_callback, - telemetry=telemetry, - ) - return orchestration - except Exception as e: - self.logger.error(f"[FAILED] Failed to create analysis orchestration: {e}") - raise RuntimeError(f"Analysis orchestration creation failed: {e}") from e - - async def _create_analysis_agents( - self, mcp_context, process_context, agent_response_callback=None, telemetry=None - ) -> GroupChatOrchestration: - """Create agents specifically for Analysis phase""" - - agents = [] - - # Technical Architect - Analysis lead - agent_architect = await mcp_context.create_agent( - agent_config=architect_agent(phase="analysis").render(**process_context), - service_id="default", - ) - agents.append(agent_architect) - - # Platform experts for source platform identification - agent_eks = await mcp_context.create_agent( - agent_config=eks_expert(phase="analysis").render(**process_context), - service_id="default", - ) - agents.append(agent_eks) - - agent_gke = await mcp_context.create_agent( - agent_config=gke_expert(phase="analysis").render(**process_context), - service_id="default", - ) - agents.append(agent_gke) - - # Azure Expert for migration context - agent_azure = await mcp_context.create_agent( - agent_config=azure_expert(phase="analysis").render(**process_context), - service_id="default", - ) - agents.append(agent_azure) - - # Technical Writer for documentation - agent_writer = await mcp_context.create_agent( - agent_config=technical_writer(phase="analysis").render(**process_context), - service_id="default", - ) - agents.append(agent_writer) - - # Create analysis-specific orchestration - orchestration = GroupChatOrchestration( - members=agents, - manager=AnalysisStepGroupChatManager( - step_name="Analysis", - step_objective="Analyze source Kubernetes configurations and identify migration requirements", - service=self.kernel_agent.kernel.services["default"], - max_rounds=50, - process_context=process_context, - telemetry=telemetry, - ), - agent_response_callback=agent_response_callback, - ) - - return orchestration -``` +Each migration step owns an orchestrator class responsible for: -#### Design Step Orchestration - -```python -class DesignOrchestrator(StepGroupChatOrchestrator): - """Orchestrator specifically for Design step operations.""" - - async def _create_design_agents( - self, mcp_context, process_context, agent_response_callback=None, telemetry=None - ) -> GroupChatOrchestration: - """Create agents specifically for Design phase""" - - agents = [] - - # Technical Architect - Design oversight - agent_architect = await mcp_context.create_agent( - agent_config=architect_agent(phase="design").render(**process_context), - service_id="default", - ) - agents.append(agent_architect) - - # Azure Expert - PRIMARY LEAD for Design phase - agent_azure = await mcp_context.create_agent( - agent_config=azure_expert(phase="design").render(**process_context), - service_id="default", - ) - agents.append(agent_azure) - - # Platform experts - Provide source platform context for design decisions - agent_eks = await mcp_context.create_agent( - agent_config=eks_expert(phase="design").render(**process_context), - service_id="default", - ) - agents.append(agent_eks) - - agent_gke = await mcp_context.create_agent( - agent_config=gke_expert(phase="design").render(**process_context), - service_id="default", - ) - agents.append(agent_gke) - - # Notice: No QA Engineer or YAML Expert in Design phase - # This shows how each phase can have its own focused agent team - - # Create design-specific orchestration - orchestration = GroupChatOrchestration( - members=agents, - manager=DesignStepGroupChatManager( - step_name="Design", - step_objective="Design Azure architecture and service mappings for migration", - service=self.kernel_agent.kernel.services["default"], - max_rounds=100, # Design needs more rounds for architecture decisions - process_context=process_context, - telemetry=telemetry, - ), - agent_response_callback=agent_response_callback, - ) - - return orchestration -``` - -#### YAML Step Orchestration - -```python -class YamlOrchestrator(StepGroupChatOrchestrator): - """Orchestrator specifically for YAML conversion step operations.""" - - async def _create_yaml_agents( - self, mcp_context, process_context, agent_response_callback=None, telemetry=None - ) -> GroupChatOrchestration: - """Create agents specifically for YAML conversion phase""" - - agents = [] - - # YAML Expert - PRIMARY LEAD for YAML phase - agent_yaml = await mcp_context.create_agent( - agent_config=yaml_expert(phase="yaml").render(**process_context), - service_id="default", - ) - agents.append(agent_yaml) - - # QA Engineer - Quality validation - agent_qa = await mcp_context.create_agent( - agent_config=qa_engineer(phase="yaml").render(**process_context), - service_id="default", - ) - agents.append(agent_qa) - - # Azure Expert - Azure service configuration validation - agent_azure = await mcp_context.create_agent( - agent_config=azure_expert(phase="yaml").render(**process_context), - service_id="default", - ) - agents.append(agent_azure) - - # Technical Writer - Documentation of conversion decisions - agent_writer = await mcp_context.create_agent( - agent_config=technical_writer(phase="yaml").render(**process_context), - service_id="default", - ) - agents.append(agent_writer) - - # Create YAML-specific orchestration - orchestration = GroupChatOrchestration( - members=agents, - manager=YamlStepGroupChatManager( - step_name="YAML", - step_objective="Convert and optimize Kubernetes YAML configurations for Azure", - service=self.kernel_agent.kernel.services["default"], - max_rounds=50, - process_context=process_context, - telemetry=telemetry, - ), - agent_response_callback=agent_response_callback, - ) - - return orchestration -``` - -### 2. MCP Context Management - -The system uses TaskGroup-safe MCP context management to provide agents with access to external tools: - -#### PluginContext Implementation - -```python -class PluginContext: - """ - Task-Group-Safe context manager for MCP plugins and Semantic Kernel plugins. - - This context manager uses TASK-GROUP-SAFE management for MCP plugins to completely - avoid the "cancel scope in different task" errors caused by manual lifecycle - management of anyio-based MCP plugins. - """ - - def __init__( - self, - kernel_agent: semantic_kernel_agent, - plugins: list[MCPPluginBase | KernelPlugin | Any] | None = None, - auto_add_to_kernel: bool = True, - ): - self.kernel_agent = kernel_agent - self.kernel = kernel_agent.kernel - self.plugins = plugins or [] - self.auto_add_to_kernel = auto_add_to_kernel - - # Task-Group-Safe resource management - self._mcp_plugins: dict[str, MCPPluginBase] = {} - self._kernel_plugins: dict[str, Any] = {} - self._agents: list[Any] = [] - self._is_entered = False - - async def __aenter__(self): - """Enter the context manager with TaskGroup scope management.""" - logger.info("Entering MCPContext with TaskGroup scope management") - - try: - await self._setup_all_plugins() - self._is_entered = True - return self - except Exception as e: - logger.error(f"[FAILED] Failed to enter MCPContext: {e}") - raise - - async def __aexit__(self, exc_type, exc_val, exc_tb): - """Exit the context manager with proper cleanup.""" - logger.info("Exiting MCPContext with TaskGroup scope cleanup") - - # Clean up MCP plugins in reverse order - for name, plugin in reversed(list(self._mcp_plugins.items())): - try: - await plugin.close() - logger.info(f"[SUCCESS] MCP plugin '{name}' closed successfully") - except Exception as e: - logger.warning(f"[WARNING] Failed to close MCP plugin '{name}': {e}") - - self._is_entered = False - logger.info("[SUCCESS] MCPContext cleanup completed") - - async def create_agent( - self, - agent_config: agent_info, - service_id: str = "default", - ) -> ChatCompletionAgent: - """Create an agent within this context with MCP plugin access.""" - if not self._is_entered: - raise RuntimeError("MCPContext must be entered before creating agents") - - logger.info(f"[ROBOT] Creating agent: {agent_config.agent_name}") - - # Collect all available plugins for the agent - plugins_for_agent = [] - - # Add connected MCP plugins - for name, plugin in self._mcp_plugins.items(): - plugins_for_agent.append(plugin) - logger.debug(f"[SUCCESS] Added connected MCP plugin to agent: {name}") - - # Create agent using AgentBuilder - agent_builder = await AgentBuilder.create_agent( - kernel_agent=self.kernel_agent, - agent_info=agent_config, - service_id=service_id, - plugins=plugins_for_agent, - ) - - if agent_builder: - self._agents.append(agent_builder) - logger.info(f"[SUCCESS] Agent '{agent_config.agent_name}' created with MCP access") - return agent_builder.agent - else: - raise RuntimeError(f"Failed to create agent: {agent_config.agent_name}") -``` - -#### MCP Plugin Setup - -```python -async def _setup_mcp_plugin(self, plugin: MCPPluginBase, name: str) -> bool: - """Setup an MCP plugin with TaskGroup scope management.""" - try: - logger.info(f"Connecting to MCP server: {name}") - - # CRITICAL: Connect to the MCP server in same task context - await plugin.connect() - logger.info(f"[SUCCESS] MCP server connected successfully: {name}") - - # Store the connected plugin - self._mcp_plugins[name] = plugin - - # Add to kernel if auto-add is enabled - if self.auto_add_to_kernel: - self.kernel.add_plugin(plugin=plugin, plugin_name=name) - logger.debug(f"Connected MCP plugin '{name}' added to kernel") - - return True - - except Exception as e: - logger.error(f"[FAILED] Failed to connect MCP plugin '{name}': {e}") - return False -``` +- Preparing MCP tools required by the step (e.g., Blob IO, Microsoft Learn, Fetch) +- Loading and rendering agent prompt files +- Running the multi-agent conversation via `GroupChatOrchestrator` -#### Usage Pattern +Example (simplified; see `src/processor/src/steps/*/orchestration/*_orchestrator.py` under [src/processor/src/steps/](../src/processor/src/steps/)): ```python -# Step-level MCP context creation -def create_task_local_mcp_context(self) -> PluginContext: - """Create task-local MCP context for TaskGroup-safe operations.""" - if self.kernel_agent is None: - raise RuntimeError("Kernel agent not initialized") +from libs.agent_framework.groupchat_orchestrator import GroupChatOrchestrator - return PluginContext( - kernel_agent=self.kernel_agent, - plugins=[ - with_name(MCPDatetimePlugin.get_datetime_plugin(), "datetime"), - with_name(MCPBlobIOPlugin.get_blob_file_operation_plugin(), "blob"), - with_name(MCPMicrosoftDocs.get_microsoft_docs_plugin(), "msdocs"), - ], - ) -# Step execution with MCP context -async def invoke(self, context: KernelProcessStepContext) -> None: - """Execute step with TaskGroup-safe MCP context.""" +class SomeStepOrchestrator: + async def execute(self, task_param): + prompt = self.render_step_prompt(task_param) + tools = await self.prepare_mcp_tools() + agents = await self.prepare_agent_infos(task_param, tools) - # Create task-local MCP context - async with self.create_task_local_mcp_context() as mcp_context: - - # Create orchestration with MCP context - orchestration = await self.orchestrator.create_orchestration_with_context( - mcp_context=mcp_context, - process_context=self.process_context, - telemetry=self.telemetry_manager, - ) - - # Run orchestration - result = await self.orchestrator.run_step_orchestration( - orchestration=orchestration, - task=self.build_step_prompt(), - step_name="Analysis" - ) - - # Process results - await self.process_step_results(result, context) -``` - -### 3. Step-Specific Group Chat Management - -Each step uses specialized group chat managers that handle conversation flow, agent selection, and termination criteria: - -#### Base Group Chat Manager - -```python -class StepSpecificGroupChatManager(GroupChatManager): - """ - Base group chat manager for step-specific orchestrations. - - Following best practices: - - Focused on single step responsibility - - Clear termination criteria per step - - Appropriate agent selection for step context - - Intelligent chat history management that preserves function calls - - Agent response telemetry tracking - """ - - def __init__( - self, - step_name: str, - step_objective: str, - service: ChatCompletionClientBase, - max_rounds: int = 50, - process_context: dict[str, Any] = None, - telemetry: TelemetryManager = None, - **kwargs, - ) -> None: - self.step_name = step_name - self.step_objective = step_objective - self.process_context = process_context or {} - self.telemetry = telemetry - super().__init__(service, **kwargs) - - async def select_next_agent( - self, - chat_history: ChatHistory, - participant_descriptions: dict[str, str], - ) -> StringResult: - """Select the next agent to speak based on step context.""" - - # Track agent responses first - await self._track_agent_message_if_new(chat_history) - - # Add step-specific selection prompt - chat_history.messages.insert( - 0, - ChatMessageContent( - role=AuthorRole.SYSTEM, - content=await self._render_prompt( - STEP_SELECTION_PROMPT, - step_name=self.step_name, - step_objective=self.step_objective, - participants="\n".join( - [f"{k}: {v}" for k, v in participant_descriptions.items()] - ), - ), - ), - ) - - # Smart truncation to manage context - self._smart_truncate_chat_history(chat_history) - - # Get agent selection - response = await get_chat_message_content_with_retry( - self.service, - chat_history, - settings=PromptExecutionSettings(response_format=StringResult), - config=get_orchestration_retry_config(), - operation_name="select_next_agent", - ) - - # Parse and validate selection - participant_selection = parse_agent_selection_safely( - response.content, - step_name=self.step_name, - valid_agents=list(participant_descriptions.keys()), - ) - - selected_agent = participant_selection.result.strip() - - # Track selection in telemetry - await self.telemetry.update_agent_activity( - process_id=self.process_context.get("process_id"), - agent_name=selected_agent, - action="selected_for_turn", - message_preview=f"Selected to speak next: {participant_selection.reason}", - ) - - return StringResult( - result=selected_agent, - reason=participant_selection.reason - ) - - def _smart_truncate_chat_history( - self, - chat_history: ChatHistory, - max_messages: int = 20, - preserve_system: bool = True, - preserve_recent_functions: bool = True, - ) -> None: - """Intelligently truncate chat history while preserving important context.""" - - if len(chat_history.messages) <= max_messages: - return - - # Separate message types - system_messages = [] - function_messages = [] - regular_messages = [] - - for msg in chat_history.messages: - if msg.role == AuthorRole.SYSTEM: - system_messages.append(msg) - elif hasattr(msg, "function_call") or msg.role == AuthorRole.TOOL: - function_messages.append(msg) - else: - regular_messages.append(msg) - - # Calculate available space - available_space = max_messages - if preserve_system and system_messages: - available_space -= 1 # Reserve space for latest system message - - # Preserve recent function call pairs (critical for migration workflow) - preserved_functions = [] - if preserve_recent_functions and function_messages: - preserved_functions = function_messages[-min(6, len(function_messages)):] - available_space -= len(preserved_functions) - - # Take most recent regular messages - preserved_regular = regular_messages[-available_space:] if available_space > 0 else [] - - # Rebuild chat history - new_messages = [] - - if preserve_system and system_messages: - new_messages.append(system_messages[-1]) # Latest system message - - new_messages.extend(preserved_regular) - new_messages.extend(preserved_functions) - - # Replace chat history messages - chat_history.messages.clear() - chat_history.messages.extend(new_messages) - - logger.info(f"[MEMORY] Chat history truncated to {len(new_messages)} messages") - - async def _track_agent_message_if_new(self, chat_history: ChatHistory): - """Track agent messages for telemetry and monitoring.""" - - if not chat_history.messages or not self.telemetry: - return - - recent_message = chat_history.messages[-1] - - # Extract agent name and message content - if hasattr(recent_message, "name") and recent_message.name: - agent_name = recent_message.name - elif hasattr(recent_message, "metadata") and recent_message.metadata: - agent_name = recent_message.metadata.get("agent_name", "Unknown") - else: - return # Skip if we can't identify the agent - - # Get message content safely - message_content = "" - if hasattr(recent_message, "content") and recent_message.content: - message_content = str(recent_message.content) - - if message_content: - # Create message preview - message_preview = ( - message_content[:150] + "..." - if len(message_content) > 150 - else message_content - ) - - # Determine activity type based on content - content_lower = message_content.lower() - action_type = "responding" - - if any(word in content_lower for word in ["analyzing", "examining", "investigating"]): - action_type = "analyzing" - elif any(word in content_lower for word in ["designing", "planning", "creating"]): - action_type = "designing" - elif any(word in content_lower for word in ["found", "discovered", "detected"]): - action_type = "reporting_findings" - elif any(word in content_lower for word in ["let me", "i will", "i'll check"]): - action_type = "thinking" - elif any(word in content_lower for word in ["completed", "finished", "done"]): - action_type = "completed" - elif "function_call" in str(recent_message) or "tool" in content_lower: - action_type = "using_tools" - - # Track agent activity - await self.telemetry.update_agent_activity( - process_id=self.process_context.get("process_id"), - agent_name=agent_name, - action=action_type, - message_preview=message_preview, + # MCP tools are async context managers; keep them open for the duration + async with (tools[0], tools[1], tools[2]): + orchestrator = GroupChatOrchestrator( + name="SomeStepOrchestrator", + process_id=task_param.process_id, + participants=agents, + memory_client=None, + result_output_format=SomeStepResultModel, ) - logger.info(f"[TELEMETRY] {agent_name} -> {action_type}: {message_preview}") -``` - -#### Specialized Step Managers - -```python -class AnalysisStepGroupChatManager(StepSpecificGroupChatManager): - """Group chat manager specialized for Analysis Step.""" - - async def should_terminate(self, chat_history: ChatHistory) -> Analysis_ExtendedBooleanResult: - """Determine if analysis step is complete.""" - - # Add termination analysis prompt - chat_history.messages.insert( - 0, - ChatMessageContent( - role=AuthorRole.SYSTEM, - content=await self._render_prompt( - ANALYSIS_TERMINATION_PROMPT, - step_name=self.step_name, - step_objective=self.step_objective, - source_file_folder=self.process_context.get("source_file_folder", ""), - output_file_folder=self.process_context.get("output_file_folder", ""), - ), - ), - ) - - # Get termination decision - response = await get_chat_message_content_with_retry( - self.service, - chat_history, - settings=PromptExecutionSettings(response_format=Analysis_ExtendedBooleanResult), - config=get_orchestration_retry_config(), - operation_name="should_terminate_analysis", - ) - - return response.content - -class DesignStepGroupChatManager(StepSpecificGroupChatManager): - """Group chat manager specialized for Design Step.""" - - async def should_terminate(self, chat_history: ChatHistory) -> Design_ExtendedBooleanResult: - """Determine if design step is complete.""" - - # Add design-specific termination logic - chat_history.messages.insert( - 0, - ChatMessageContent( - role=AuthorRole.SYSTEM, - content=await self._render_prompt( - DESIGN_TERMINATION_PROMPT, - step_name=self.step_name, - step_objective=self.step_objective, - source_file_folder=self.process_context.get("source_file_folder", ""), - output_file_folder=self.process_context.get("output_file_folder", ""), - ), - ), - ) - - response = await get_chat_message_content_with_retry( - self.service, - chat_history, - settings=PromptExecutionSettings(response_format=Design_ExtendedBooleanResult), - config=get_orchestration_retry_config(), - operation_name="should_terminate_design", - ) - - return response.content -``` - -### Agent Creation and Configuration - -Agents are configured using phase-specific information: - -```python -# Each agent has a get_agent_info function that returns phase-specific configuration -def get_agent_info(phase: MigrationPhase | str | None = None) -> agent_info: - """Get agent configuration for specific migration phase""" - - if phase == "analysis": - prompt_file = "./prompt-analysis.txt" - instruction = "Focus on platform detection and source configuration analysis" - elif phase == "design": - prompt_file = "./prompt-design.txt" - instruction = "Focus on Azure architecture design and service mapping" - elif phase == "yaml": - prompt_file = "./prompt-yaml.txt" - instruction = "Focus on YAML conversion and configuration optimization" - else: - prompt_file = "./prompt-documentation.txt" - instruction = "Focus on comprehensive documentation generation" - - return agent_info( - agent_name="Azure_Expert", - agent_type=AgentType.ChatCompletionAgent, - agent_system_prompt=load_prompt_text(prompt_file), - agent_instruction=instruction, - ) - -# Agent template rendering with context -def render_agent_config(agent_config: agent_info, **context) -> agent_info: - """Render agent configuration with context variables""" - - template = Template(agent_config.agent_system_prompt) - rendered_prompt = template.render(**context) - - return agent_info( - agent_name=agent_config.agent_name, - agent_type=agent_config.agent_type, - agent_system_prompt=rendered_prompt, - agent_instruction=agent_config.agent_instruction, - ) -``` - -### Agent Types Implementation - -#### Supported Agent Types - -```python -class AgentType(str, Enum): - AzureAssistantAgent = "AzureAssistantAgent" # Azure Assistant API based agents - AzureAIAgent = "AzureAIAgent" # Azure AI agent service - ChatCompletionAgent = "ChatCompletionAgent" # Standard chat completion agents -``` - -#### Agent Setup Logic - -```python -async def _set_up_agent( - self, - agent_info: agent_info, - service_id: str = "default", - plugins: list[KernelPlugin | object | dict[str, Any]] | None = None, - response_format: AgentsApiResponseFormatOption | None = None, -): - """Set up agent based on agent_info.agent_type""" - if plugins is None: - plugins = [] - - self.meta_data = agent_info - - match agent_info.agent_type: - case AgentType.AzureAssistantAgent: - await self._setup_azure_assistant_agent(agent_info, plugins) - case AgentType.AzureAIAgent: - await self._setup_azure_ai_agent(agent_info, plugins) - case AgentType.ChatCompletionAgent: - await self._setup_chat_completion_agent( - agent_info=agent_info, service_id=service_id, plugins=plugins + return await orchestrator.run_stream( + input_data=prompt, + on_agent_response=self.on_agent_response, + on_workflow_complete=self.on_orchestration_complete, + on_agent_response_stream=self.on_agent_response_stream, ) - case _: - raise ValueError(f"Unsupported agent type: {agent_info.agent_type}") - -async def _setup_chat_completion_agent( - self, - agent_info: agent_info, - service_id: str = "default", - plugins: list[KernelPlugin | object | dict[str, Any]] = None, -): - """Setup Chat Completion Agent""" - self.agent = self.kernel_agent.get_azure_ai_inference_chat_completion_agent( - agent_name=agent_info.agent_name, - agent_instructions=agent_info.agent_system_prompt, - service_id=service_id, - plugins=plugins, - ) ``` -## Agent Telemetry and Monitoring +### 2. MCP Tool Integration -### TelemetryManager Implementation +The processor integrates MCP servers as Agent Framework tools. Typical tools include: -The system includes comprehensive telemetry tracking for agent activities using the actual `TelemetryManager` class: +- `Microsoft Learn MCP` (HTTP) +- `Fetch MCP Tool` (stdio) +- `azure_blob_io_service` (Blob IO operations; internal MCP wrapper) +- `datetime_service` (timestamps/time-window logic) +- `yaml_inventory_service` (ground runbooks in real converted objects) -```python -# Actual TelemetryManager methods from your implementation -class TelemetryManager: - """Clean telemetry manager for agent activity tracking.""" - - async def init_process(self, process_id: str, phase: str, step: str): - """Initialize telemetry for a new process.""" +### 3. Platform-Specific Experts (Registry Driven) - async def update_agent_activity( - self, - process_id: str, - agent_name: str, - action: str, - message_preview: str = "", - tool_used: bool = False, - tool_name: str = "", - reset_for_new_step: bool = False, - ): - """Update agent activity.""" +Some steps dynamically include platform experts based on signals (e.g., EKS, GKE, OpenShift). The registry-driven approach keeps this extensible. - async def track_tool_usage( - self, - process_id: str, - agent_name: str, - tool_name: str, - tool_action: str, - tool_details: str = "", - tool_result_preview: str = "", - ): - """Track tool usage by agents.""" +## Key Takeaways - async def render_agent_status(self, process_id: str) -> dict: - """Enhanced agent status rendering with context-aware messages.""" +This orchestration model enables reliable step-by-step collaboration: - async def record_step_result( - self, process_id: str, step_name: str, step_result: dict - ): - """Record the result of a completed step.""" +- Step orchestrators prepare tools and prompts, then run a `GroupChatOrchestrator` per phase +- MCP servers are exposed as Agent Framework tools +- Platform experts are selected via a registry to keep extensions modular +- Quality gates require PASS/FAIL sign-offs before the workflow proceeds - async def record_final_outcome( - self, process_id: str, outcome_data: dict, success: bool = True - ): - """Record the final migration outcome with comprehensive results.""" -``` - -### Agent Activity Tracking - -```python -class AgentActivity(EntityBase): - """Current activity status of an agent""" - - name: str - current_action: str = "idle" - last_message_preview: str = "" - last_full_message: str = "" - current_speaking_content: str = "" - last_update_time: str = Field(default_factory=_get_utc_timestamp) - is_active: bool = False - is_currently_speaking: bool = False - is_currently_thinking: bool = False - thinking_about: str = "" - current_reasoning: str = "" - last_reasoning: str = "" - reasoning_steps: list[str] = Field(default_factory=list) - participation_status: str = "ready" - last_activity_summary: str = "" - message_word_count: int = 0 - activity_history: list[AgentActivityHistory] = Field(default_factory=list) - step_reset_count: int = 0 - -class AgentActivityHistory(EntityBase): - """Historical record of agent activity""" - - timestamp: str = Field(default_factory=_get_utc_timestamp) - action: str - message_preview: str = "" - step: str = "" - tool_used: str = "" -``` - -## Best Practices and Implementation Guidelines - -### 1. Agent Design Principles - -```python -agent_azure = await mcp_context.create_agent( - agent_config=azure_expert(phase="yaml").render(**process_context), - service_id="default", -) - -# Agent configuration with phase-specific rendering -agent_config = get_agent_info(phase="analysis").render(**process_context) -``` - -### 2. Step-Specific Orchestration Patterns - -```python -class StepGroupChatOrchestrator: - async def run_step_orchestration( - self, orchestration, task: str, step_name: str - ) -> Any: - """Execute step-specific orchestration with telemetry tracking.""" - return result -``` - -### 3. MCP Context Management - -```python -async with asyncio.TaskGroup() as tg: - # Create MCP context within task group - mcp_context = tg.create_task( - self._create_mcp_context_for_step(self.app_context) - ) - - # Use context for agent creation - orchestration = await self.create_orchestration_with_context( - await mcp_context, - telemetry, - ) -``` - -### 4. Chat History Management -```python -def _smart_truncate_chat_history( - self, chat_history: ChatHistory, max_tokens: int = 8000 -) -> ChatHistory: - """Intelligently truncate chat history while preserving context.""" - return truncated_history -``` This multi-agent orchestration approach provides a sophisticated, extensible foundation for complex container migration scenarios while maintaining clean separation of concerns and robust error handling. @@ -1054,4 +169,3 @@ The Container Migration Solution Accelerator's multi-agent orchestration system - **Extensible Design**: Modular architecture supporting new platforms and agent types This approach ensures reliable, scalable container migrations while maintaining high quality through collaborative AI expertise. -``` diff --git a/docs/ProcessFrameworkGuide.md b/docs/ProcessFrameworkGuide.md index cb4b8cf..575d4a1 100644 --- a/docs/ProcessFrameworkGuide.md +++ b/docs/ProcessFrameworkGuide.md @@ -1,912 +1,374 @@ -# Process Framework Implementation Guide +# Workflow & Executor Implementation Guide (Microsoft Agent Framework) -This guide provides detailed information about the process framework used in the Container Migration Solution Accelerator, including step definitions, execution patterns, and customization options. +This guide explains how the Container Migration Solution Accelerator processor implements step-based execution using **Microsoft Agent Framework** workflows, orchestrations, and workflow executors. -## Overview - -The Container Migration Solution Accelerator uses a step-based process framework that breaks down complex migration tasks into manageable, sequential phases. Each step is designed to be independent, testable, and extensible. - -## Process Framework Architecture - -### Core Components - -The Process Framework consists of six core components organized in three functional layers: - -#### Framework Components - -| Component | Purpose | Layer | -| ------------------------ | -------------------------------------------------- | --------------- | -| **Process Orchestrator** | Coordinates overall migration process execution | Control Layer | -| **Step Executor** | Manages individual step execution and lifecycle | Execution Layer | -| **Agent Orchestrator** | Handles multi-agent collaboration and coordination | Execution Layer | -| **State Manager** | Maintains process state and progress tracking | Support Layer | -| **Context Manager** | Manages execution context and data flow | Support Layer | -| **Error Handler** | Provides centralized error handling and recovery | Support Layer | +> Scope: This document describes the **processor runtime and step execution model**. Infrastructure deployment details remain unchanged. -#### Component Interactions +## References (Official Documentation) -The components interact through well-defined relationships: +- [Microsoft Agent Framework overview](https://learn.microsoft.com/en-us/agent-framework/overview/agent-framework-overview) +- [Workflow core concepts (overview)](https://learn.microsoft.com/en-us/agent-framework/user-guide/workflows/core-concepts/overview) +- [Executors (core concept)](https://learn.microsoft.com/en-us/agent-framework/user-guide/workflows/core-concepts/executors) +- [Workflows (core concept)](https://learn.microsoft.com/en-us/agent-framework/user-guide/workflows/core-concepts/workflows) +- [Workflow orchestrations (patterns)](https://learn.microsoft.com/en-us/agent-framework/user-guide/workflows/orchestrations/overview) +- [Group chat orchestration](https://learn.microsoft.com/en-us/agent-framework/user-guide/workflows/orchestrations/group-chat) +- [Multi-agent orchestration patterns (architecture guidance)](https://learn.microsoft.com/en-us/azure/architecture/ai-ml/guide/ai-agent-design-patterns) -**Primary Flow:** -- Process Orchestrator → Step Executor → Agent Orchestrator - -**State Management:** -- Process Orchestrator → State Manager -- Agent Orchestrator → Context Manager - -**Error Handling:** -- Step Executor → Error Handler -- State Manager → Error Handler -- Context Manager → Error Handler - -### Step-Based Processing - -The framework divides migration into four main steps: - -1. **Analysis Step**: Platform detection and configuration analysis -2. **Design Step**: Azure architecture design and service mapping -3. **YAML Step**: Configuration transformation and Azure integration -4. **Documentation Step**: Comprehensive documentation generation +## Overview -### Step Execution Flow -![Step Execution Flow](images/readme/step_execution_flow.png) +The processor executes a migration as a **sequential workflow** of four steps: + +1. **Analysis**: discover inputs and detect the source platform +2. **Design**: propose Azure target architecture and migration approach +3. **YAML conversion**: generate converted manifests (AKS-ready) +4. **Documentation**: produce the final migration report and runbook + +This repo implements that pipeline using: + +- A top-level **Agent Framework Workflow** that routes messages between steps. +- A step-level **Executor** per phase (analysis/design/yaml/documentation). +- A step-level **Orchestrator** (per step) that runs a multi-agent collaboration pattern (typically group chat). + +Conceptually: + +- **Workflow** = the directed graph of computation (executors + edges) that defines what runs next. +- **Executor** = the unit of work for a step; it receives an input message and emits an output message. +- **Orchestration** = a multi-agent pattern used inside a step (for example group chat), where multiple agents collaborate to produce the step output. + +## Process Execution Flow + +```mermaid +flowchart TB + %% Process Execution Flow (v2) - Microsoft Agent Framework + + Q[("Azure Storage Queue
job message")] + MP["Migration Processor
run_stream(...)"] + WB["Agent Framework Workflow
WorkflowBuilder.build()"] + + Q --> MP --> WB + + subgraph WF["Workflow: Analysis -> Design -> YAML -> Documentation"] + direction TB + AEX["Analysis Executor
(Executor)"] + DEX["Design Executor
(Executor)"] + YEX["YAML Convert Executor
(Executor)"] + DOX["Documentation Executor
(Executor)"] + + AEX -->|ctx.send_message| DEX + DEX -->|ctx.send_message| YEX + YEX -->|ctx.send_message| DOX + end + + WB --> AEX + + OUT["Final Output
Documentation_ExtendedBooleanResult"] + DOX -->|ctx.yield_output| OUT + AEX -.->|ctx.yield_output hard-terminate| OUT + DEX -.->|ctx.yield_output hard-terminate| OUT + YEX -.->|ctx.yield_output hard-terminate| OUT + + %% Inside each step: orchestrator + group chat + tools + subgraph STEP["Within each executor"] + direction TB + ORCH["Step Orchestrator
(e.g., AnalysisOrchestrator)"] + GCO["Group Chat Orchestrator
(GroupChatOrchestrator)"] + AG["Specialist Agents
(Chief Architect, AKS Expert, etc.)"] + MCP["MCP tools
(Microsoft Learn, Fetch, Blob IO, DateTime, ...)"] + ORCH --> GCO --> AG --> MCP + end + + AEX --- ORCH + DEX --- ORCH + YEX --- ORCH + DOX --- ORCH + + %% Telemetry + TELEM["Telemetry events"] + COSMOS[("Cosmos DB
telemetry/state")] + MP --> TELEM --> COSMOS + + %% Styling (subtle theme) + %% - Infra/services: blue accent + %% - Workflow executors: neutral with blue border + %% - Orchestration internals: warm accent + classDef core fill:#ffffff,stroke:#111827,color:#111827,stroke-width:1px; + classDef infra fill:#e0f2fe,stroke:#0284c7,color:#111827,stroke-width:1px; + classDef exec fill:#ffffff,stroke:#0284c7,color:#111827,stroke-width:1px; + classDef orch fill:#fff7ed,stroke:#f59e0b,color:#111827,stroke-width:1px; + classDef meta fill:#f8fafc,stroke:#94a3b8,color:#111827,stroke-width:1px; + + class Q,COSMOS infra; + class MCP infra; + class MP,WB meta; + class AEX,DEX,YEX,DOX,OUT exec; + class ORCH,GCO,AG orch; + class TELEM meta; + + style WF fill:#f8fafc,stroke:#94a3b8,color:#111827; + style STEP fill:#fff7ed,stroke:#f59e0b,color:#111827; +``` -The step execution follows a sequential pattern with built-in error handling and state management: +At the top level, the workflow is deterministic and step-by-step: **analysis → design → yaml → documentation**. -1. **Process Initialization**: Context setup and validation -2. **Analysis Step**: Platform detection and configuration analysis -3. **Design Step**: Azure architecture design and service mapping -4. **YAML Step**: Configuration transformation and Azure integration -5. **Documentation Step**: Comprehensive documentation generation -6. **Process Completion**: Final validation and cleanup +Inside each step, the orchestrator can use multi-agent patterns (maker-checker loops, review sign-offs, etc.) to produce the step result. -Each step maintains its own state and can recover from errors independently, while the Process Orchestrator manages the overall flow and coordinates between steps. +## How Agent Framework concepts map to this repository -## Step Implementation Pattern +### Workflow -### Base Step Structure +- Implementation: [src/processor/src/steps/migration_processor.py](../src/processor/src/steps/migration_processor.py) +- The processor creates a workflow with `WorkflowBuilder`. +- It registers four executors, sets the start executor, and defines edges. -Each step follows the Semantic Kernel Process Framework pattern using `KernelProcessStep`: +Example from the repo (simplified): ```python -from semantic_kernel.processes.kernel_process import ( - KernelProcessStep, - KernelProcessStepContext, - KernelProcessStepState, +from agent_framework import WorkflowBuilder + +workflow = ( + WorkflowBuilder() + .register_executor(lambda: AnalysisExecutor(id="analysis", app_context=app_context), name="analysis") + .register_executor(lambda: DesignExecutor(id="design", app_context=app_context), name="design") + .register_executor(lambda: YamlConvertExecutor(id="yaml", app_context=app_context), name="yaml") + .register_executor(lambda: DocumentationExecutor(id="documentation", app_context=app_context), name="documentation") + .set_start_executor("analysis") + .add_edge("analysis", "design") + .add_edge("design", "yaml") + .add_edge("yaml", "documentation") + .build() ) -from pydantic import BaseModel, Field -from libs.steps.base_step_state import BaseStepState -from utils.tool_tracking import ToolTrackingMixin - -class StepState(BaseStepState): - """State for the step following SK Process Framework best practices.""" - - # Base fields required by KernelProcessStepState - name: str = Field(default="StepState", description="Name of the step state") - version: str = Field(default="1.0", description="Version of the step state") - - # Step-specific state fields - result: bool | None = None # None = not started, True = success, False = failed - reason: str = "" - # Additional step-specific fields... - -class Step(KernelProcessStep[StepState], ToolTrackingMixin): - """ - Step implementation following SK Process Framework best practices. - - Features: - - Single responsibility principle - - Isolated kernel instance to prevent recursive invocation - - Proper error handling and event emission - - Pydantic-based parameter validation - """ - - state: StepState | None = Field(default_factory=lambda: StepState()) - - async def activate(self, state: KernelProcessStepState[StepState]): - """Activate the step for state initialization.""" - self.state = state.state - self._ensure_state_initialized() - - def _ensure_state_initialized(self) -> None: - """Ensure state is properly initialized before use.""" - if self.state is None: - self.state = StepState(name="StepState", version="1.0") ``` -### Parameter Validation Pattern +### Executors (step runtime) -All steps implement Pydantic-based parameter validation: +Executors are the fundamental building blocks that process typed messages in a workflow. -```python -from pydantic import BaseModel, ValidationError - -class StepParameter(BaseModel): - """Pydantic model for step parameter validation""" - model_config = {"arbitrary_types_allowed": True, "extra": "allow"} - - process_id: str - source_file_folder: str - workspace_file_folder: str - output_file_folder: str - container_name: str - # Additional parameters... - -def _create_parameters(self, context_data: dict[str, Any]) -> tuple[bool, StepParameter | None]: - """ - Create and validate parameters using Pydantic model validation. - - Returns: - tuple[bool, StepParameter | None]: (is_valid, validated_parameters) - """ - try: - # Use Pydantic's model_validate for comprehensive type checking - validated_params = StepParameter.model_validate(context_data) - return True, validated_params - - except ValidationError as e: - # Enhanced error logging with detailed Pydantic validation errors - error_details = [] - for error in e.errors(): - field = error.get("loc", ["unknown"])[0] if error.get("loc") else "unknown" - message = error.get("msg", "Validation failed") - error_details.append(f"{field}: {message}") - - logger.error(f"Parameter validation failed: {'; '.join(error_details)}") - return False, None -``` +- Official docs: [Executors (core concept)](https://learn.microsoft.com/en-us/agent-framework/user-guide/workflows/core-concepts/executors) -## Step Implementations +In this repo, each step executor: -### 1. Analysis Step +- Inherits from `agent_framework.Executor` +- Implements a handler decorated with `@handler` +- Receives: + - `message`: typed input payload + - `ctx`: `WorkflowContext[...]` used to forward messages or yield final output -The analysis step detects platforms and analyzes configurations using real implementation: +Repo implementations: -```python -from libs.steps.analysis_step import AnalysisStep, AnalysisStepState -from libs.steps.orchestration.analysis_orchestration import AnalysisOrchestrator - -class AnalysisStepState(BaseStepState): - """State for the Analysis step following best practices.""" - - name: str = Field(default="AnalysisStepState", description="Name of the step state") - version: str = Field(default="1.0", description="Version of the step state") - - result: bool | None = None # None = not started, True = success, False = failed - reason: str = "" - platform_detected: str = "" - files_discovered: list = [] - analysis_completed: bool = False - final_result: Analysis_ExtendedBooleanResult | None = None - -class AnalysisStep(KernelProcessStep[AnalysisStepState], ToolTrackingMixin): - """ - Analysis step that discovers YAML files and detects source platform. - - Following SK Process Framework best practices: - - Single responsibility: file discovery and platform detection only - - Isolated kernel instance to prevent recursive invocation - - Proper error handling and event emission - """ - - @kernel_function(name="start_migration_analysis") - async def start_migration_analysis( - self, context: KernelProcessStepContext, context_data: dict[str, Any] - ): - """Execute analysis with comprehensive error handling and orchestration.""" - - # Parameter validation using Pydantic - is_valid, params = self._create_analysis_parameters(context_data) - if not is_valid: - await self._emit_analysis_failure(context, "Parameter validation failed") - return - - try: - # Initialize orchestrator with MCP plugins - orchestrator = AnalysisOrchestrator( - process_id=params.process_id, - app_context=params.app_context, - plugin_context=PluginContext() - ) - - # Execute analysis - result = await orchestrator.execute_analysis(params) - - # Update state and emit success - self.state.result = True - self.state.platform_detected = result.platform_detected - self.state.files_discovered = result.files_discovered - self.state.analysis_completed = True - - await context.emit_event( - KernelProcessEvent(id="analysis_completed", data=result.model_dump()) - ) - - except Exception as e: - # Comprehensive error handling - await self._handle_analysis_error(context, e, params) -``` +- [src/processor/src/steps/analysis/workflow/analysis_executor.py](../src/processor/src/steps/analysis/workflow/analysis_executor.py) +- [src/processor/src/steps/design/workflow/design_executor.py](../src/processor/src/steps/design/workflow/design_executor.py) +- [src/processor/src/steps/convert/workflow/yaml_convert_executor.py](../src/processor/src/steps/convert/workflow/yaml_convert_executor.py) +- [src/processor/src/steps/documentation/workflow/documentation_executor.py](../src/processor/src/steps/documentation/workflow/documentation_executor.py) -### 2. Design Step - -The design step creates Azure architecture recommendations using the actual implementation: +A typical executor shape (based on `AnalysisExecutor`): ```python -from libs.steps.design_step import DesignStep, DesignStepState -from libs.steps.orchestration.design_orchestration import DesignOrchestrator - -class DesignStepState(BaseStepState): - """State for the Design step following best practices.""" - - name: str = Field(default="DesignStepState", description="Name of the step state") - version: str = Field(default="1.0", description="Version of the step state") - - result: bool | None = None - reason: str = "" - architecture_created: str = "" - design_recommendations: list[str] = Field(default_factory=list) - azure_services: list[str] = Field(default_factory=list) - migration_strategy: str = "" - design_completed: bool = False - -class DesignStep(KernelProcessStep[DesignStepState], ToolTrackingMixin): - """ - Design step for creating Azure architecture and service mapping. - - Features: - - Azure service recommendations - - Architecture pattern suggestions - - Cost estimation integration - - Migration strategy planning - """ - - @kernel_function(name="start_migration_design") - async def start_migration_design( - self, context: KernelProcessStepContext, context_data: dict[str, Any] - ): - """Execute design phase with Azure service integration.""" - - try: - # Initialize design orchestrator with MCP plugins - orchestrator = DesignOrchestrator( - process_id=context_data["process_id"], - app_context=context_data["app_context"], - plugin_context=PluginContext() - ) - - # Execute design with multi-agent collaboration - result = await orchestrator.execute_design(context_data) - - # Update state with design results - self.state.result = True - self.state.architecture_created = result.architecture - self.state.azure_services = result.recommended_services - self.state.migration_strategy = result.strategy - self.state.design_completed = True - - await context.emit_event( - KernelProcessEvent(id="design_completed", data=result.model_dump()) - ) - - except Exception as e: - await self._handle_design_error(context, e) -``` - -### 3. YAML Step +from agent_framework import Executor, WorkflowContext, handler -The YAML step transforms configurations to Azure-compatible formats using the real implementation: -```python -from libs.steps.yaml_step import YamlStep, YamlStepState -from libs.steps.orchestration.yaml_orchestration import YamlOrchestrator - -class YamlStepState(BaseStepState): - """State for the YAML step following best practices.""" - - name: str = Field(default="YamlStepState", description="Name of the step state") - version: str = Field(default="1.0", description="Version of the step state") - - result: bool | None = None - reason: str = "" - files_converted: list[str] = Field(default_factory=list) - azure_integrations: list[str] = Field(default_factory=list) - yaml_completed: bool = False - -class YamlStep(KernelProcessStep[YamlStepState], ToolTrackingMixin): - """ - YAML step for converting configurations to Azure-compatible formats. - - Features: - - Kubernetes to AKS transformation - - Azure service integration - - Configuration validation - - Multi-file processing - """ - - @kernel_function(name="start_migration_yaml") - async def start_migration_yaml( - self, context: KernelProcessStepContext, context_data: dict[str, Any] - ): - """Execute YAML transformation with Azure integration.""" - - try: - # Initialize YAML orchestrator with MCP plugins - orchestrator = YamlOrchestrator( - process_id=context_data["process_id"], - app_context=context_data["app_context"], - plugin_context=PluginContext() - ) - - # Execute YAML transformation - result = await orchestrator.execute_yaml_conversion(context_data) - - # Update state with conversion results - self.state.result = True - self.state.files_converted = result.converted_files - self.state.azure_integrations = result.azure_services_integrated - self.state.yaml_completed = True - - await context.emit_event( - KernelProcessEvent(id="yaml_completed", data=result.model_dump()) - ) - - except Exception as e: - await self._handle_yaml_error(context, e) -``` - -### 4. Documentation Step - -The documentation step generates comprehensive migration documentation using the actual implementation: +class AnalysisExecutor(Executor): + def __init__(self, id: str, app_context: AppContext): + super().__init__(id=id) + self.app_context = app_context -```python -from libs.steps.documentation_step import DocumentationStep, DocumentationStepState -from libs.steps.orchestration.documentation_orchestration import DocumentationOrchestrator - -class DocumentationStepState(BaseStepState): - """State for the Documentation step following best practices.""" - - name: str = Field(default="DocumentationStepState", description="Name of the step state") - version: str = Field(default="1.0", description="Version of the step state") - - result: bool | None = None - reason: str = "" - documents_generated: list[str] = Field(default_factory=list) - documentation_completed: bool = False - -class DocumentationStep(KernelProcessStep[DocumentationStepState], ToolTrackingMixin): - """ - Documentation step for generating comprehensive migration documentation. - - Features: - - Migration analysis reports - - Architecture documentation - - Implementation guides - - Troubleshooting documentation - """ - - @kernel_function(name="start_migration_documentation") - async def start_migration_documentation( - self, context: KernelProcessStepContext, context_data: dict[str, Any] - ): - """Execute documentation generation with comprehensive reporting.""" - - try: - # Initialize documentation orchestrator - orchestrator = DocumentationOrchestrator( - process_id=context_data["process_id"], - app_context=context_data["app_context"], - plugin_context=PluginContext() - ) - - # Execute documentation generation - result = await orchestrator.execute_documentation(context_data) - - # Update state with documentation results - self.state.result = True - self.state.documents_generated = result.generated_documents - self.state.documentation_completed = True - - await context.emit_event( - KernelProcessEvent(id="documentation_completed", data=result.model_dump()) - ) - - except Exception as e: - await self._handle_documentation_error(context, e) + @handler + async def handle_execute( + self, + message: Analysis_TaskParam, + ctx: WorkflowContext[Analysis_BooleanExtendedResult], + ) -> None: + result = await AnalysisOrchestrator(self.app_context).execute(task_param=message) + + # Continue the workflow with the next step input + if result.result and not result.result.is_hard_terminated: + await ctx.send_message(result.result) + + # Or stop the workflow early with a final output + elif result.result: + await ctx.yield_output(result.result) ``` -## Process Orchestration +### Orchestrations (multi-agent collaboration inside a step) -### Main Process Implementation +- Official docs overview: [Workflow orchestrations (patterns)](https://learn.microsoft.com/en-us/agent-framework/user-guide/workflows/orchestrations/overview) +- Group chat orchestration: [Group chat orchestration](https://learn.microsoft.com/en-us/agent-framework/user-guide/workflows/orchestrations/group-chat) -The main migration process uses the Semantic Kernel Process Framework: +In this repository, each executor delegates the “AI-heavy” work to a step-specific orchestrator. Those orchestrators typically: -```python -from semantic_kernel.processes import ProcessBuilder -from libs.steps.analysis_step import AnalysisStep -from libs.steps.design_step import DesignStep -from libs.steps.yaml_step import YamlStep -from libs.steps.documentation_step import DocumentationStep +- Prepare MCP tools (e.g., Microsoft Learn, Fetch, Blob IO) +- Load and render prompts +- Construct agent participants (coordinator + specialists) +- Run a multi-agent orchestration (commonly group chat) to produce a typed step output -class AKSMigrationProcess: - """ - Main migration process following SK Process Framework best practices. +Repo example: - Process Flow: - Analysis → Design → YAML Generation → Documentation +- [src/processor/src/libs/agent_framework/groupchat_orchestrator.py](../src/processor/src/libs/agent_framework/groupchat_orchestrator.py) - Each step is isolated and communicates via events only. - """ +This repo uses group chat for iterative refinement and review-heavy flows (for example documentation sign-off loops), aligning with Microsoft’s guidance that group chat is appropriate for collaborative problem-solving and maker-checker workflows. - @staticmethod - def create_process(): - """ - Create the migration process with proper event routing. +## Execution model in this repo - Following best practices: - - Each step has single responsibility - - Events handle all inter-step communication - - Error handling at each step - - Clear process boundaries - """ +### Step-to-step message flow - # Create ProcessBuilder with required parameters - process_builder = ProcessBuilder(name="AKSMigrationProcess") +At the top level, the workflow is sequential: - # Add steps using default constructors - analysis_step = process_builder.add_step(AnalysisStep) - design_step = process_builder.add_step(DesignStep) - yaml_step = process_builder.add_step(YamlStep) - documentation_step = process_builder.add_step(DocumentationStep) +- Analysis emits an `Analysis_BooleanExtendedResult` +- Design consumes it and emits a `Design_ExtendedBooleanResult` +- YAML conversion consumes it and emits a `Yaml_ExtendedBooleanResult` +- Documentation consumes it and yields `Documentation_ExtendedBooleanResult` as the final workflow output - # Configure event routing between steps - process_builder.on_input_event("start_migration") \ - .send_event_to(analysis_step, "start_migration_analysis") +### Hard termination vs failure - analysis_step.on_event("analysis_completed") \ - .send_event_to(design_step, "start_migration_design") +This repo supports two distinct stop paths: - design_step.on_event("design_completed") \ - .send_event_to(yaml_step, "start_migration_yaml") +- **Hard termination (graceful)**: a step decides it cannot proceed and yields an output marked as hard-terminated. The executor calls `ctx.yield_output(...)` to end the workflow with a meaningful final result. +- **Failure (exceptional)**: an executor or workflow fails unexpectedly; the processor captures failure context and reports via telemetry. - yaml_step.on_event("yaml_completed") \ - .send_event_to(documentation_step, "start_migration_documentation") +See the exception wrappers and failure summary logic in [src/processor/src/steps/migration_processor.py](../src/processor/src/steps/migration_processor.py). - documentation_step.on_event("documentation_completed") \ - .stop_process() +## Adding a new step (executor) - return process_builder.build() -``` +To add a new step: -### Migration Service Execution +1. Create a new executor in `src/processor/src/steps//workflow/_executor.py` (under [src/processor/src/steps/](../src/processor/src/steps/)). +2. Create/update typed models: + - `src/processor/src/steps//models/step_param.py` (input) + - `src/processor/src/steps//models/step_output.py` (output) +3. Implement the step orchestrator (if multi-agent): + - `src/processor/src/steps//orchestration/_orchestrator.py` +4. Register and wire the executor in [src/processor/src/steps/migration_processor.py](../src/processor/src/steps/migration_processor.py) using `WorkflowBuilder`. -The migration service coordinates process execution with comprehensive error handling: +## Notes on MCP tools -```python -from services.migration_service import MigrationProcessor, MigrationEngineResult -from libs.processes.aks_migration_process import AKSMigrationProcess - -class MigrationProcessor: - """ - Migration processor with queue-based processing and timeout protection. - - Features: - - Azure OpenAI integration - - DefaultAzureCredential authentication - - Comprehensive error classification - - Telemetry and monitoring - """ - - async def process_migration_request(self, request_data: dict) -> MigrationEngineResult: - """ - Process migration request using SK Process Framework. - - Returns: - MigrationEngineResult with comprehensive execution metadata - """ - process_id = request_data.get("process_id", str(uuid.uuid4())) - start_time = time.time() - - try: - # Create and start the migration process - process = AKSMigrationProcess.create_process() - - # Execute with event-driven flow - runtime = InProcessRuntime() - process_handle = await runtime.start_process(process, request_data) - - # Wait for completion or timeout - final_state = await self._wait_for_completion(process_handle) - - execution_time = time.time() - start_time - - return MigrationEngineResult( - success=True, - process_id=process_id, - execution_time=execution_time, - status=ProcessStatus.COMPLETED, - final_state=final_state - ) - - except Exception as e: - # Comprehensive error classification - error_classification = classify_error(e) - execution_time = time.time() - start_time - - return MigrationEngineResult( - success=False, - process_id=process_id, - execution_time=execution_time, - status=ProcessStatus.FAILED, - error_message=str(e), - error_classification=error_classification - ) -``` +Microsoft Agent Framework supports tool calling and MCP server integration as first-class concepts. -## Key Framework Features +- Agent Framework MCP docs: [Model Context Protocol (MCP)](https://learn.microsoft.com/en-us/agent-framework/user-guide/model-context-protocol/) -### 1. MCP Plugin Integration +In this repo, MCP servers are integrated as **Agent Framework tools** used by orchestrators and agents. -The framework integrates with Model Context Protocol (MCP) for Azure services: +## See also -```python -from plugins.mcp_server import MCPBlobIOPlugin, MCPDatetimePlugin, MCPMicrosoftDocs -from utils.mcp_context import PluginContext, with_name +- Multi-agent orchestration guide in this repo: [docs/MultiAgentOrchestration.md](MultiAgentOrchestration.md) +- Overall processor architecture: [docs/AgenticArchitecture.md](AgenticArchitecture.md) +- MCP tool configuration and server guide: [docs/ConfigureMCPServers.md](ConfigureMCPServers.md) and [docs/MCPServerGuide.md](MCPServerGuide.md) -class StepOrchestrator: - """Base orchestrator with MCP plugin support""" +## Step implementations (real code) - def __init__(self, process_id: str, app_context: AppContext, plugin_context: PluginContext): - self.process_id = process_id - self.app_context = app_context - self.plugin_context = plugin_context +This repository no longer uses the previous process-framework step model for the processor pipeline. - # Initialize MCP plugins for Azure integration - self.blob_plugin = MCPBlobIOPlugin() - self.docs_plugin = MCPMicrosoftDocs() - self.datetime_plugin = MCPDatetimePlugin() -``` +Instead, each step is implemented as: -### 2. Tool Tracking and Telemetry +1. A **workflow executor** (Agent Framework `Executor`) that receives a typed message. +2. A step **orchestrator** that runs a multi-agent collaboration pattern (typically group chat) and returns a typed step result. -Steps include comprehensive tool tracking and telemetry: +Where to look: -```python -from utils.tool_tracking import ToolTrackingMixin -from utils.agent_telemetry import TelemetryManager - -class Step(KernelProcessStep[StepState], ToolTrackingMixin): - """Step with tool tracking capabilities""" - - def __init__(self): - super().__init__() - self.telemetry_manager = TelemetryManager() - - async def track_operation(self, operation_name: str, operation_data: dict): - """Track operation with telemetry""" - await self.telemetry_manager.record_operation( - step_name=self.__class__.__name__, - operation=operation_name, - data=operation_data - ) -``` +- Top-level workflow wiring: [src/processor/src/steps/migration_processor.py](../src/processor/src/steps/migration_processor.py) +- Step executors: + - [src/processor/src/steps/analysis/workflow/analysis_executor.py](../src/processor/src/steps/analysis/workflow/analysis_executor.py) + - [src/processor/src/steps/design/workflow/design_executor.py](../src/processor/src/steps/design/workflow/design_executor.py) + - [src/processor/src/steps/convert/workflow/yaml_convert_executor.py](../src/processor/src/steps/convert/workflow/yaml_convert_executor.py) + - [src/processor/src/steps/documentation/workflow/documentation_executor.py](../src/processor/src/steps/documentation/workflow/documentation_executor.py) +- Step orchestrators (multi-agent logic): `src/processor/src/steps/**/orchestration/` (under [src/processor/src/steps/](../src/processor/src/steps/)) +- Group chat orchestration implementation: [src/processor/src/libs/agent_framework/groupchat_orchestrator.py](../src/processor/src/libs/agent_framework/groupchat_orchestrator.py) -### 3. Error Classification and Recovery +Executor handlers use `WorkflowContext` to either: -Comprehensive error handling with classification: +- Continue the pipeline via `ctx.send_message(...)`, or +- Stop early with a final result via `ctx.yield_output(...)`. -```python -from utils.error_classifier import ErrorClassification, classify_error -from libs.models.failure_context import StepFailureState - -async def _handle_step_error(self, context: KernelProcessStepContext, error: Exception): - """Handle step errors with comprehensive classification""" - - # Classify error for handling strategy - error_classification = classify_error(error) - - # Create rich failure context - failure_context = StepFailureState( - error_message=str(error), - error_type=error_classification.error_type, - severity=error_classification.severity, - is_retryable=error_classification.is_retryable, - step_name=self.__class__.__name__, - timestamp=time.time() - ) - - # Update step state with failure context - self.state.failure_context = failure_context - self.state.result = False - self.state.reason = str(error) - - # Emit failure event - await context.emit_event( - KernelProcessEvent( - id="step_failed", - data={ - "step_name": self.__class__.__name__, - "error": str(error), - "classification": error_classification.model_dump() - } - ) - ) -``` +## Workflow execution and event streaming -## State Management +The processor runs the top-level workflow using event streaming in `MigrationProcessor.run(...)`: -### Shared Process State +- Implementation: [src/processor/src/steps/migration_processor.py](../src/processor/src/steps/migration_processor.py) +- Execution: `async for event in self.workflow.run_stream(input_data): ...` -The framework uses a shared state model that flows between steps: +The processor consumes events such as: -```python -from libs.processes.models.migration_state import MigrationProcessState - -class MigrationProcessState(BaseModel): - """ - Shared state that flows between all migration process steps. - - This represents the complete state of a migration process, - designed to be passed as events between SK Process steps. - """ - - # Process Identity - process_id: str - user_request: str = "" - - # Step Control - current_step: str = "initialization" - - # Shared Configuration - source_platform: str = "" # eks, gke, etc. - target_platform: str = "azure" - workspace_file_folder: str = "workspace" - - # Analysis Step Results - platform_detected: str = "" - files_discovered: list[str] = Field(default_factory=list) - file_count: int = 0 - analysis_summary: str = "" - analysis_completed: bool = False - - # Design Step Results - architecture_created: str = "" - design_recommendations: list[str] = Field(default_factory=list) - azure_services: list[str] = Field(default_factory=list) - migration_strategy: str = "" - design_completed: bool = False - - # YAML Step Results - files_converted: list[str] = Field(default_factory=list) - azure_integrations: list[str] = Field(default_factory=list) - yaml_completed: bool = False - - # Documentation Step Results - documents_generated: list[str] = Field(default_factory=list) - documentation_completed: bool = False -``` +- `WorkflowStartedEvent` +- `ExecutorInvokedEvent` +- `ExecutorCompletedEvent` +- `ExecutorFailedEvent` +- `WorkflowOutputEvent` +- `WorkflowFailedEvent` -### Step-Specific State +Those events are used to: -Each step maintains its own isolated state that extends the base pattern: +- Drive step/phase telemetry (`TelemetryManager`). +- Populate structured reporting (`MigrationReportCollector` / `MigrationReportGenerator`). -```python -from libs.steps.base_step_state import BaseStepState -from libs.models.failure_context import StepFailureState - -class BaseStepState(KernelProcessStepState): - """Base state for all migration steps""" - - # Base fields required by KernelProcessStepState - name: str = Field(default="BaseStepState", description="Name of the step state") - version: str = Field(default="1.0", description="Version of the step state") - - # Rich failure context for error handling - failure_context: StepFailureState | None = Field( - default=None, description="Rich failure context when step fails" - ) - - # Comprehensive timing infrastructure - execution_start_time: float | None = Field(default=None) - execution_end_time: float | None = Field(default=None) - orchestration_start_time: float | None = Field(default=None) - orchestration_end_time: float | None = Field(default=None) -``` - -## Monitoring and Reporting +This is also where the processor differentiates between: -### Process Monitoring +- **Hard termination** (a step yields an output early via `ctx.yield_output(...)`), and +- **Exceptional failure** (a step errors, producing `ExecutorFailedEvent` / `WorkflowFailedEvent`). -The framework includes comprehensive monitoring and telemetry: +## MCP tools integration (Agent Framework tools) -```python -from utils.agent_telemetry import TelemetryManager -from libs.reporting import MigrationReportGenerator, MigrationReportCollector +In this repo, MCP servers are integrated as **Agent Framework tools**. -class ProcessMonitor: - """Monitors process execution and collects telemetry""" +Where to look: - def __init__(self): - self.telemetry_manager = TelemetryManager() - self.report_collector = MigrationReportCollector() +- Tool wrappers: [src/processor/src/libs/mcp_server/](../src/processor/src/libs/mcp_server/) +- Tool usage: `src/processor/src/steps/**/orchestration/` (under [src/processor/src/steps/](../src/processor/src/steps/)) - async def monitor_step_execution(self, step_name: str, step_state: BaseStepState): - """Monitor individual step execution""" +Two connection styles are used: - # Record step metrics - await self.telemetry_manager.record_step_metrics( - step_name=step_name, - execution_time=step_state.execution_end_time - step_state.execution_start_time, - success=step_state.result, - failure_context=step_state.failure_context - ) +1. **HTTP MCP tools** (remote) + - Example: Microsoft Learn MCP endpoint + - Used via `MCPStreamableHTTPTool(name="Microsoft Learn MCP", url="https://learn.microsoft.com/api/mcp")` - # Collect reporting data - self.report_collector.add_step_result(step_name, step_state) -``` +2. **Stdio MCP tools** (local subprocess) + - Example: blob IO, datetime, mermaid validation, yaml inventory + - Used via `MCPStdioTool(command="uv", args=[...])` + - Local MCP servers in this repo are implemented using FastMCP under [src/processor/src/libs/mcp_server/](../src/processor/src/libs/mcp_server/). -### Migration Reporting - -Comprehensive reporting system for migration results: +When a step runs, orchestrators typically open tool sessions like this: ```python -from libs.reporting.models.Processes import Process, Step -from libs.reporting import FailureType, FailureSeverity - -class MigrationReportGenerator: - """Generates comprehensive migration reports""" - - async def generate_migration_report(self, process_state: MigrationProcessState) -> dict: - """Generate comprehensive migration report""" - - report = { - "process_summary": { - "process_id": process_state.process_id, - "source_platform": process_state.source_platform, - "target_platform": process_state.target_platform, - "files_processed": process_state.file_count, - "overall_status": self._determine_overall_status(process_state) - }, - "step_results": { - "analysis": { - "completed": process_state.analysis_completed, - "platform_detected": process_state.platform_detected, - "files_discovered": len(process_state.files_discovered) - }, - "design": { - "completed": process_state.design_completed, - "azure_services": len(process_state.azure_services), - "architecture_created": bool(process_state.architecture_created) - }, - "yaml": { - "completed": process_state.yaml_completed, - "files_converted": len(process_state.files_converted), - "azure_integrations": len(process_state.azure_integrations) - }, - "documentation": { - "completed": process_state.documentation_completed, - "documents_generated": len(process_state.documents_generated) - } - } - } - - return report +async with ( + self.mcp_tools[0], + self.mcp_tools[1], + self.mcp_tools[2], +): + ... ``` -## Customization Guide +## Reporting and telemetry -### Adding Custom Steps +Reporting and telemetry are driven by workflow execution events and captured in the processor: -To add a new processing step to the framework: +- Telemetry: [src/processor/src/utils/agent_telemetry.py](../src/processor/src/utils/agent_telemetry.py) (`TelemetryManager`) +- Reporting: [src/processor/src/libs/reporting/](../src/processor/src/libs/reporting/) (`MigrationReportCollector`, `MigrationReportGenerator`) +- Event loop: [src/processor/src/steps/migration_processor.py](../src/processor/src/steps/migration_processor.py) -1. **Create Step State Class**: Inherit from `BaseStepState` -2. **Create Step Class**: Inherit from `KernelProcessStep` and `ToolTrackingMixin` -3. **Implement Kernel Functions**: Add `@kernel_function` decorated methods -4. **Add Error Handling**: Implement comprehensive error classification -5. **Register in Process**: Add to `AKSMigrationProcess` -6. **Create Orchestrator**: Implement step-specific orchestration if needed +## Evaluation and quality checks -### Example Custom Step +The processor’s “quality checks” are a combination of workflow-level structure plus step-level validation: -```python -from semantic_kernel.functions import kernel_function -from libs.steps.base_step_state import BaseStepState -from semantic_kernel.processes.kernel_process import ( - KernelProcessStep, - KernelProcessStepContext, - KernelProcessEvent -) +- **Typed boundaries**: each executor consumes and emits typed models, which keeps step-to-step contracts explicit. +- **Multi-agent review**: orchestrators can implement maker-checker loops and sign-offs (commonly via group chat). +- **Tool-backed validation**: steps can call MCP tools (e.g., Mermaid validation, YAML inventory grounding, documentation lookups) to validate outputs against real inputs and references. +- **Unit tests**: core behavior is covered by tests under [src/processor/src/tests/unit/](../src/processor/src/tests/unit/). -class CustomStepState(BaseStepState): - """State for custom processing step""" - - name: str = Field(default="CustomStepState", description="Name of the step state") - version: str = Field(default="1.0", description="Version of the step state") - - result: bool | None = None - reason: str = "" - custom_data: str = "" - custom_completed: bool = False - -class CustomStep(KernelProcessStep[CustomStepState], ToolTrackingMixin): - """ - Custom step for specialized processing requirements. - - Following SK Process Framework best practices: - - Single responsibility principle - - Event-driven communication - - Comprehensive error handling - - Pydantic-based validation - """ - - state: CustomStepState | None = Field( - default_factory=lambda: CustomStepState(name="CustomStepState", version="1.0") - ) - - async def activate(self, state: KernelProcessStepState[CustomStepState]): - """Activate the step for state initialization.""" - self.state = state.state - self._ensure_state_initialized() - - @kernel_function(name="start_custom_processing") - async def start_custom_processing( - self, context: KernelProcessStepContext, context_data: dict[str, Any] - ): - """Execute custom processing with error handling.""" - - try: - # Parameter validation - is_valid, params = self._validate_parameters(context_data) - if not is_valid: - await self._emit_failure(context, "Parameter validation failed") - return - - # Execute custom logic - result = await self._perform_custom_processing(params) - - # Update state - self.state.result = True - self.state.custom_data = result - self.state.custom_completed = True - - # Emit success event - await context.emit_event( - KernelProcessEvent(id="custom_completed", data={"result": result}) - ) - - except Exception as e: - await self._handle_custom_error(context, e) - - def _ensure_state_initialized(self) -> None: - """Ensure state is properly initialized before use.""" - if self.state is None: - self.state = CustomStepState(name="CustomStepState", version="1.0") -``` - -### Integration with Process - -Add the custom step to the migration process: +To run processor unit tests locally (example): -```python -# In AKSMigrationProcess.create_process() -custom_step = process_builder.add_step(CustomStep) +```bash +cd src/processor +uv run --prerelease=allow python -m pytest src/processor/src/tests/unit -v +``` -# Configure event routing -yaml_step.on_event("yaml_completed") \ - .send_event_to(custom_step, "start_custom_processing") +## Extending the pipeline -custom_step.on_event("custom_completed") \ - .send_event_to(documentation_step, "start_migration_documentation") -``` +Use the steps in “Adding a new step (executor)” above, then wire the executor into the workflow graph in [src/processor/src/steps/migration_processor.py](../src/processor/src/steps/migration_processor.py). -## Next Steps +## Troubleshooting notes -1. **Understand Step Pattern**: Learn the step implementation pattern -2. **Review Existing Steps**: Study the current step implementations -3. **Identify Custom Needs**: Determine if custom steps are needed -4. **Implement Extensions**: Add custom steps and orchestration patterns -5. **Monitor and Optimize**: Implement monitoring and optimize performance +- If a step appears to “hang”, check MCP tool startup (stdio tools spawn subprocesses via `uv`/`uvx`). +- If an executor fails, the processor surfaces structured details via `WorkflowExecutorFailedException`. +- If the workflow completes but the output is missing, the processor raises `WorkflowOutputMissingException`. -For additional information, refer to: +## Next steps - [Multi-Agent Orchestration Approach](MultiAgentOrchestration.md) - [Technical Architecture](TechnicalArchitecture.md) diff --git a/docs/QuotaCheck.md b/docs/QuotaCheck.md index 35b0e0f..c1659a6 100644 --- a/docs/QuotaCheck.md +++ b/docs/QuotaCheck.md @@ -2,7 +2,7 @@ Before deploying the accelerator, **ensure sufficient quota availability** for the required model. -> **For Global Standard | o3 - the capacity to at least 500K tokens for optimal performance.** +> **For Global Standard | gpt-5.1 - target at least 500K tokens for optimal performance.** ### Login if you have not done so already @@ -17,7 +17,7 @@ az login --use-device-code ### 📌 Default Models & Capacities: ``` -o3:500 +gpt-5.1:500 ``` ### 📌 Default Regions: @@ -53,7 +53,7 @@ eastus, uksouth, eastus2, northcentralus, swedencentral, westus, westus2, southc ✔️ Check specific model(s) in default regions: ``` -./quota_check_params.sh --models o3:500 +./quota_check_params.sh --models gpt-5.1:500 ``` ✔️ Check default models in specific region(s): @@ -65,13 +65,13 @@ eastus, uksouth, eastus2, northcentralus, swedencentral, westus, westus2, southc ✔️ Passing Both models and regions: ``` -./quota_check_params.sh --models o3:500 --regions eastus,westus2 +./quota_check_params.sh --models gpt-5.1:500 --regions eastus,westus2 ``` ✔️ All parameters combined: ``` -./quota_check_params.sh --models o3:500 --regions eastus,westus --verbose +./quota_check_params.sh --models gpt-5.1:500 --regions eastus,westus --verbose ``` ### **Sample Output** diff --git a/docs/SampleWorkflow.md b/docs/SampleWorkflow.md index 296db49..aa207b5 100644 --- a/docs/SampleWorkflow.md +++ b/docs/SampleWorkflow.md @@ -5,7 +5,7 @@ To help you get started, here’s a **sample process** you can follow in the app ## **Process** -> Note: Download sample data files for **EKS & GKE** in data folder from [here](../data). +> Note: Sample data files provided in the repo are for **EKS & GKE** and live in the data folder ([here](../data)). The solution also supports other source Kubernetes platforms (e.g., OpenShift/Rancher/Tanzu/on-prem), but sample data for those may not be included. ### **Upload** diff --git a/docs/TechnicalArchitecture.md b/docs/TechnicalArchitecture.md index 96dd8ec..6df592f 100644 --- a/docs/TechnicalArchitecture.md +++ b/docs/TechnicalArchitecture.md @@ -4,458 +4,145 @@ This document provides a comprehensive technical overview of the Container Migra ## Overview -The Container Migration Solution Accelerator is built on a modern, cloud-native architecture that leverages artificial intelligence, multi-agent orchestration, and the Model Context Protocol (MCP) to automate container platform migrations to Azure. +The Container Migration Solution Accelerator is built on a modern, cloud-native, queue-driven architecture that leverages artificial intelligence, multi-agent orchestration, and the Model Context Protocol (MCP) to automate container platform migrations to Azure. ## High-Level Architecture + ```mermaid graph TB - subgraph "User Interface Layer" - UI[User Interface] + UI[Frontend UI] --> API[Backend API] + API --> Q[Azure Storage Queue] + + subgraph Processor[Processor (Queue Worker)] + QW[Queue Worker] + WF[Agent Framework Workflow\nanalysis → design → yaml → docs] + CA[Control API] + PC[Process Control Store] + QW --> WF + CA --> PC end - subgraph "Migration Orchestrator" - MO[Migration Orchestrator
• Process flow management
• Step coordination
• Agent orchestration] - end + Q --> QW - subgraph "Process Steps" - AS[Analysis Step
• Platform detection
• Source assessment] - DS[Design Step
• Azure architecture
• Service mapping] - YS[YAML Step
• Configuration conversion
• YAML validation] - DOS[Documentation Step
• Migration reports
• User guides] + subgraph Tools[Tools (MCP + local tools)] + Blob[Blob IO] + Docs[Microsoft Docs] + Mermaid[Mermaid] + Datetime[Datetime] + YamlInv[YAML Inventory] end - subgraph "Step-Specific Agent Groups" - subgraph "Analysis Agents" - TA1[Technical Architect] - EKS1[EKS Expert] - GKE1[GKE Expert] - end - - subgraph "Design Agents" - TA2[Technical Architect] - AE1[Azure Expert] - EKS2[EKS Expert] - GKE2[GKE Expert] - end - - subgraph "YAML Agents" - AE2[Azure Expert] - QA1[QA Engineer] - TW1[Technical Writer] - YE[YAML Expert] - end - - subgraph "Documentation Agents" - TA3[Technical Architect] - AE3[Azure Expert] - EKS3[EKS Expert] - GKE3[GKE Expert] - QA2[QA Engineer] - TW2[Technical Writer] - end + subgraph External[External Services] + ST[Azure Blob Storage] + Models[Azure OpenAI / Azure AI Foundry Models] end - subgraph "Model Context Protocol (MCP)" - BMC[Azure Blob Storage
MCP Server] - DMC[Microsoft Docs
MCP Server] - TMC[Datetime Utilities
MCP Server] - end + WF --> Tools + Blob --> ST + Docs --> Models +``` - subgraph "External Services" - AZBS[Azure Blob Storage] - MSDN[Microsoft Learn Docs] - AI[AI Models
GPT o3] - end - UI --> MO - MO --> AS - AS --> DS - DS --> YS - YS --> DOS - - AS --> TA1 - AS --> EKS1 - AS --> GKE1 - - DS --> TA2 - DS --> AE1 - DS --> EKS2 - DS --> GKE2 - - YS --> AE2 - YS --> QA1 - YS --> TW1 - YS --> YE - - DOS --> TA3 - DOS --> AE3 - DOS --> EKS3 - DOS --> GKE3 - DOS --> QA2 - DOS --> TW2 - - TA1 --> BMC - TA1 --> DMC - TA1 --> TMC - TA1 --> AI - TA2 --> BMC - TA2 --> DMC - TA2 --> TMC - TA2 --> AI - TA3 --> BMC - TA3 --> DMC - TA3 --> TMC - TA3 --> AI - - AE1 --> BMC - AE1 --> DMC - AE1 --> AI - AE2 --> BMC - AE2 --> DMC - AE2 --> AI - AE3 --> BMC - AE3 --> DMC - AE3 --> AI - - EKS1 --> BMC - EKS1 --> AI - EKS2 --> BMC - EKS2 --> AI - EKS3 --> BMC - EKS3 --> AI - - GKE1 --> BMC - GKE1 --> AI - GKE2 --> BMC - GKE2 --> AI - GKE3 --> BMC - GKE3 --> AI - - QA1 --> BMC - QA1 --> AI - QA2 --> BMC - QA2 --> AI - - TW1 --> BMC - TW1 --> DMC - TW1 --> AI - TW2 --> BMC - TW2 --> DMC - TW2 --> AI - - YE --> BMC - YE --> AI - - BMC --> AZBS - DMC --> MSDN -``` +## Core Components (Processor) -## Migration Workflow +### 1. Queue Worker -The end-to-end migration process follows a structured workflow with clear phases and checkpoints: -```mermaid -graph LR - Start([Migration Start]) --> Init[Initialize Process] - Init --> Discovery[Platform Discovery] - - Discovery --> Analysis[Analysis Step] - Analysis --> AnalysisAgents[Technical Architect
EKS Expert
GKE Expert] - AnalysisAgents --> AnalysisOutput[Source Platform Analysis
Configuration Discovery
Migration Assessment] - - AnalysisOutput --> Design[Design Step] - Design --> DesignAgents[Technical Architect
Azure Expert
EKS/GKE Experts] - DesignAgents --> DesignOutput[Azure Architecture Design
Service Mapping
Migration Strategy] - - DesignOutput --> YAML[YAML Conversion Step] - YAML --> YAMLAgents[Azure Expert
QA Engineer
Technical Writer
YAML Expert] - YAMLAgents --> YAMLOutput[Azure Kubernetes Manifests
Configuration Files
Deployment Resources] - - YAMLOutput --> Documentation[Documentation Step] - Documentation --> DocsAgents[Technical Architect
Azure Expert
Platform Experts
QA Engineer
Technical Writer] - DocsAgents --> DocsOutput[Migration Guide
Deployment Instructions
Operational Documentation] - - DocsOutput --> Complete([Migration Complete]) - - style Start fill:#e1f5fe - style Complete fill:#c8e6c9 - style Analysis fill:#fff3e0 - style Design fill:#fff3e0 - style YAML fill:#fff3e0 - style Documentation fill:#fff3e0 -``` +The processor runs as a queue-driven worker in hosted scenarios. -## Implementation Architecture +**Responsibilities:** -The Container Migration Solution Accelerator follows a layered architecture that aligns with the actual codebase structure: +- Poll Azure Storage Queue for jobs +- Validate/deserialize request payloads +- Execute the Agent Framework workflow +- Persist artifacts and emit telemetry -### Application Entry Points -- **main_service.py**: Service interface for hosted scenarios +**Implementation Locations:** -### Service Layer -- **migration_service.py**: Core MigrationProcessor with queue-based processing -- **queue_service.py**: Azure Storage Queue integration -- **retry_manager.py**: Retry logic and error recovery +- [src/processor/src/main_service.py](../src/processor/src/main_service.py) +- [src/processor/src/services/queue_service.py](../src/processor/src/services/queue_service.py) -### Process Framework (Semantic Kernel) -- **aks_migration_process.py**: Main process definition using ProcessBuilder -- **Step-based execution**: Analysis → Design → YAML → Documentation +**Operational Notes:** -### Agent Implementation -- **Individual agent directories**: Each expert agent has dedicated folder with prompts -- **Semantic Kernel GroupChat**: Multi-agent orchestration -- **Azure OpenAI integration**: GPT o3 model support +- The queue worker is intentionally simple; behavior such as retries and DLQ are controlled by the queue configuration/patterns and service logic. -### MCP Server Integration -- **Plugin-based architecture**: Modular MCP server implementations -- **Azure service integration**: Blob storage, documentation APIs -- **File operations**: Local and cloud file management +### 2. Control API + Process Control -### Implementation Component Map -```mermaid -flowchart LR - subgraph UI["🎯 Entry Points"] - direction TB - MAIN[main.py
CLI Interface] - SERVICE[main_service.py
Service Interface] - end +The processor exposes a lightweight control surface for health and termination. - subgraph CORE["🔄 Process Engine"] - direction TB - MIGRATION[MigrationProcessor
Core Engine] - PROCESS[AKSMigrationProcess
Workflow Definition] - MIGRATION --- PROCESS - end +**Implementation Locations:** - subgraph STEPS["📋 Migration Steps"] - direction LR - ANALYSIS[Analysis
Platform Discovery] - DESIGN[Design
Architecture Planning] - YAML[YAML
Configuration Transform] - DOCS[Documentation
Guide Generation] - ANALYSIS --> DESIGN - DESIGN --> YAML - YAML --> DOCS - end +- [src/processor/src/services/control_api.py](../src/processor/src/services/control_api.py) +- [src/processor/src/services/process_control.py](../src/processor/src/services/process_control.py) - subgraph AI["🤖 AI Layer"] - direction TB - AGENTS[Multi-Agent System
7 Specialized Agents
Semantic Kernel GroupChat] - end +### 3. Workflow Engine (Microsoft Agent Framework) - subgraph TOOLS["🔌 Tool Integration"] - direction TB - MCP[3 MCP Servers
• Blob Storage
• Microsoft Docs
• DateTime Utilities] - end +The migration pipeline is defined as an Agent Framework workflow built via `WorkflowBuilder` and executed step-by-step. - subgraph CLOUD["☁️ External Services"] - direction TB - AZURE[Azure Services
• OpenAI GPT o3
• Blob Storage
• Documentation APIs] - end +**Execution Order:** - %% Main flow connections - UI --> CORE - CORE --> STEPS - - %% AI integration (dotted for supporting role) - STEPS -.-> AI - AI --> TOOLS - TOOLS --> CLOUD - - %% Responsive styling with better contrast - classDef entryPoint fill:#e3f2fd,stroke:#1976d2,stroke-width:2px,color:#000 - classDef processCore fill:#fff3e0,stroke:#f57c00,stroke-width:2px,color:#000 - classDef migrationStep fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px,color:#000 - classDef aiLayer fill:#e8f5e8,stroke:#388e3c,stroke-width:2px,color:#000 - classDef toolLayer fill:#fce4ec,stroke:#c2185b,stroke-width:2px,color:#000 - classDef cloudLayer fill:#e1f5fe,stroke:#0288d1,stroke-width:2px,color:#000 - - class UI,MAIN,SERVICE entryPoint - class CORE,MIGRATION,PROCESS processCore - class STEPS,ANALYSIS,DESIGN,YAML,DOCS migrationStep - class AI,AGENTS aiLayer - class TOOLS,MCP toolLayer - class CLOUD,AZURE cloudLayer -``` +- analysis → design → yaml → documentation -### Data Flow Architecture -```mermaid -sequenceDiagram - participant U as User/CLI - participant MA as main.py - participant MIG as MigrationProcessor - participant PROC as AKSMigrationProcess - participant AS as AnalysisStep - participant AG as Agent (TA/EKS/GKE) - participant MCP as MCP Server - participant EXT as External Service - participant BLOB as Azure Blob - - U->>MA: Start Migration - MA->>MIG: Initialize MigrationProcessor - MIG->>PROC: Create Process Instance - PROC->>AS: Start Analysis Step - - AS->>AS: Setup Step Context - AS->>AG: Initialize Agent Group - AG->>MCP: Request File Operations - MCP->>EXT: Execute K8s Discovery - EXT-->>MCP: Return Config Files - MCP-->>AG: Processed Results - - AG->>AG: AI Analysis Processing - AG-->>AS: Analysis Results - AS->>BLOB: Save Step Results - AS-->>PROC: Step Complete Event - - PROC->>PROC: Next Step (Design) - Note over PROC: Pattern repeats for Design, YAML, Documentation steps - - PROC-->>MIG: Process Complete - MIG-->>MA: Migration Results - MA-->>U: Final Report -``` -## Core Components +**Implementation Location:** -### 1. Migration Orchestrator +- [src/processor/src/steps/migration_processor.py](../src/processor/src/steps/migration_processor.py) -The central orchestration engine that manages the entire migration workflow. +### 4. Multi-Agent Orchestration -**Responsibilities:** -- Process flow management -- Step coordination and sequencing -- Error handling and recovery -- Progress tracking and reporting -- Resource management - -**Key Classes:** -- `MigrationOrchestrator`: Main orchestration controller -- `StepExecutor`: Individual step execution management -- `ProcessState`: Migration state management -- `ErrorHandler`: Error handling and recovery +Steps that require multi-agent reasoning use a group chat style orchestrator. -**Implementation Location:** -```text -src/libs/processes/ -├── migration_orchestrator.py # Main orchestrator -├── step_executor.py # Step execution logic -├── process_state.py # State management -└── error_handler.py # Error handling -``` +**Key Concepts:** + +- Coordinator agent manages turn-taking and termination +- Platform experts contribute source-platform-specific guidance +- Result generator consolidates structured outputs + +**Implementation Locations:** + +- [src/processor/src/libs/agent_framework/groupchat_orchestrator.py](../src/processor/src/libs/agent_framework/groupchat_orchestrator.py) +- [src/processor/src/libs/agent_framework/agent_builder.py](../src/processor/src/libs/agent_framework/agent_builder.py) +- [src/processor/src/libs/agent_framework/agent_info.py](../src/processor/src/libs/agent_framework/agent_info.py) +- Platform expert registry: [src/processor/src/steps/analysis/orchestration/platform_registry.json](../src/processor/src/steps/analysis/orchestration/platform_registry.json) + +### 5. MCP Tool Integration (Agent Framework Tools) + +Tools are exposed to agents using Agent Framework tool abstractions, including MCP. + +**Processor MCP tools:** + +- [src/processor/src/libs/mcp_server/MCPBlobIOTool.py](../src/processor/src/libs/mcp_server/MCPBlobIOTool.py) +- [src/processor/src/libs/mcp_server/MCPMicrosoftDocs.py](../src/processor/src/libs/mcp_server/MCPMicrosoftDocs.py) +- [src/processor/src/libs/mcp_server/MCPMermaidTool.py](../src/processor/src/libs/mcp_server/MCPMermaidTool.py) +- [src/processor/src/libs/mcp_server/MCPDatetimeTool.py](../src/processor/src/libs/mcp_server/MCPDatetimeTool.py) +- [src/processor/src/libs/mcp_server/MCPYamlInventoryTool.py](../src/processor/src/libs/mcp_server/MCPYamlInventoryTool.py) + +## Technology Stack (Processor) + +### Core + +- Microsoft Agent Framework (workflow + orchestration) +- Python 3.12+ +- asyncio +- aiohttp (control API) + +### AI + +- Azure OpenAI / Azure AI Foundry models (project-dependent) +- Model Context Protocol (MCP) for tool access + +### Azure SDKs + +- Azure Storage Queue (job intake) +- Azure Storage Blob (artifact IO) +- Azure Identity (auth) + +### Dev/Ops -### 2. Step-Based Processing Architecture - -The migration process is divided into discrete, sequential steps: - -#### Analysis Step -- **Purpose**: Source platform analysis and configuration discovery -- **Input**: Source configuration files and platform information -- **Output**: Analysis report with platform-specific insights -- **Implementation**: `src/libs/steps/analysis_step.py` - -#### Design Step -- **Purpose**: Azure architecture design and service mapping -- **Input**: Analysis results and source configurations -- **Output**: Azure architecture recommendations and design patterns -- **Implementation**: `src/libs/steps/design_step.py` - -#### YAML Conversion Step -- **Purpose**: Configuration transformation to Azure-compatible YAML -- **Input**: Source configurations and design recommendations -- **Output**: Azure Kubernetes Service (AKS) compatible YAML files -- **Implementation**: `src/libs/steps/yaml_step.py` - -#### Documentation Step -- **Purpose**: Migration documentation and implementation guides -- **Input**: All previous step outputs and transformation decisions -- **Output**: Comprehensive migration documentation -- **Implementation**: `src/libs/steps/documentation_step.py` - -### 3. Multi-Agent System - -Built on Microsoft Semantic Kernel with GroupChat orchestration: - -#### Technical Architect Agent -- **Role**: Overall migration strategy and architectural decisions -- **Expertise**: Cloud architecture patterns, migration best practices -- **Phase Participation**: All phases with strategic oversight - -#### Azure Expert Agent -- **Role**: Azure-specific optimizations and Well-Architected Framework compliance -- **Expertise**: Azure services, cost optimization, security patterns -- **Phase Participation**: Design, YAML conversion, documentation - -#### Platform Expert Agents -- **Role**: Source platform-specific knowledge and transformation patterns -- **Variants**: EKS Expert, GKE Expert (extensible for future platforms) -- **Expertise**: Platform-specific configurations, migration patterns -- **Phase Participation**: Analysis, design, YAML conversion - -#### QA Engineer Agent -- **Role**: Quality assurance, validation, and testing strategies -- **Expertise**: Testing patterns, validation criteria, quality gates -- **Phase Participation**: All phases with validation focus - -#### Specialized Agents -- **YAML Expert**: Configuration syntax and optimization -- **Technical Writer**: Documentation quality and structure - -### 4. Model Context Protocol (MCP) Integration - -MCP provides standardized access to external tools and services: - -#### Azure Blob Storage MCP Server -- **Purpose**: Azure Blob Storage operations and file management -- **Capabilities**: Blob operations, container management, file storage -- **Implementation**: `MCPBlobIOPlugin.py` - -#### File Operations MCP Server -- **Purpose**: Local file system operations and document management -- **Capabilities**: File I/O, YAML/JSON processing, file validation -- **Implementation**: `MCPFileIOPlugin.py` - -#### Microsoft Docs MCP Server -- **Purpose**: Microsoft documentation API integration -- **Capabilities**: Documentation retrieval, content processing, reference lookup -- **Implementation**: `MCPMicrosoftDocs.py` - -#### Datetime Utilities MCP Server -- **Purpose**: Date and time operations for migration tracking -- **Capabilities**: Timestamp generation, date formatting, scheduling -- **Implementation**: `MCPDatetimePlugin.py` - -## Technology Stack - -### Core Framework -- **Microsoft Semantic Kernel**: AI orchestration and agent management -- **Python 3.12+**: Primary programming language -- **asyncio**: Asynchronous processing and concurrency -- **Pydantic**: Data validation and serialization - -### AI and ML -- **GPT o3**: Primary language model for agent reasoning -- **Azure OpenAI**: AI service integration -- **Model Context Protocol (MCP)**: Tool and resource integration - -### Configuration and Data -- **YAML/JSON**: Configuration file processing -- **Jinja2**: Template processing and generation -- **ruamel.yaml**: Advanced YAML processing with preservation - -### Azure Integration -- **Azure SDK for Python**: Azure service integration -- **Azure Identity**: Authentication and authorization -- **Azure Kubernetes Service**: Target platform APIs -- **Azure Container Registry**: Container image management - -### Development and Operations -- **uv**: Package management and virtual environments -- **pytest**: Testing framework -- **Docker**: Containerization for deployment -- **Git**: Version control and repository management +- uv (dependency management) +- Docker (containerized execution) +- pytest (tests) For additional technical details, refer to: - [Multi-Agent Orchestration Approach](MultiAgentOrchestration.md) -- [Process Framework Implementation](ProcessFrameworkGuide.md) - [MCP Server Implementation Guide](MCPServerGuide.md) - [Deployment Guide](DeploymentGuide.md) diff --git a/docs/TroubleShootingSteps.md b/docs/TroubleShootingSteps.md index 603ce90..8805a6b 100644 --- a/docs/TroubleShootingSteps.md +++ b/docs/TroubleShootingSteps.md @@ -317,18 +317,6 @@ The subscription 'xxxx-xxxx' cannot have more than 1 Container App Environments -
Conflict - Cannot use the SKU Basic with File Change Audit for site. - -- This error happens because File Change Audit logs aren’t supported on Basic SKU App Service Plans. - -- Upgrading to Premium/Isolated SKU (supports File Change Audit), or - -- Disabling File Change Audit in Diagnostic Settings if you must stay on Basic. -- Always cross-check the [supported log types](https://aka.ms/supported-log-types) - before adding diagnostic logs to your Bicep templates. - -
-
AccountPropertyCannotBeUpdated @@ -351,7 +339,7 @@ The subscription 'xxxx-xxxx' cannot have more than 1 Container App Environments This error occurs when your subscription does not have access to certain Azure OpenAI models. **Example error message:** -`SpecialFeatureOrQuotaIdRequired: The current subscription does not have access to this model 'Format:OpenAI,Name:o3,Version:2025-04-16'.` +`SpecialFeatureOrQuotaIdRequired: The current subscription does not have access to this model 'Format:OpenAI,Name:gpt-5.1,Version:2025-04-16'.` **Resolution:** To gain access, submit a request using the official form: @@ -359,8 +347,7 @@ To gain access, submit a request using the official form: You’ll need to use this form if you require access to the following restricted models: - gpt-5 -- o3 -- o3-pro +- gpt-5.1 - deep research - reasoning summary - gpt-image-1 diff --git a/docs/images/readme/agentic_architecture.png b/docs/images/readme/agentic_architecture.png index a138dab..a96342d 100644 Binary files a/docs/images/readme/agentic_architecture.png and b/docs/images/readme/agentic_architecture.png differ diff --git a/docs/images/readme/architecture.png b/docs/images/readme/architecture.png index 9b04455..420ae7e 100644 Binary files a/docs/images/readme/architecture.png and b/docs/images/readme/architecture.png differ diff --git a/infra/main.bicep b/infra/main.bicep index 26445f9..16a2440 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -32,11 +32,11 @@ var solutionLocation = empty(location) ? resourceGroup().location : location azd: { type: 'location' usageName: [ - 'OpenAI.GlobalStandard.o3, 500' + 'OpenAI.GlobalStandard.GPT5.1, 500' ] } }) -@description('Required. Azure region for AI services (OpenAI/AI Foundry). Must be a region that supports o3 model deployment.') +@description('Required. Azure region for AI services (OpenAI/AI Foundry). Must be a region that supports GPT5.1 model deployment.') param azureAiServiceLocation string @allowed([ @@ -68,8 +68,8 @@ param imageTag string = 'latest' param aiDeploymentType string = 'GlobalStandard' @minLength(1) -@description('Optional. Name of the AI model to deploy. Recommend using o3. Defaults to o3.') -param aiModelName string = 'o3' +@description('Optional. Name of the AI model to deploy. Recommend using GPT5.1. Defaults to GPT5.1.') +param aiModelName string = 'GPT5.1' @minLength(1) @description('Optional. Version of AI model. Review available version numbers per model before setting. Defaults to 2025-04-16.') @@ -78,6 +78,16 @@ param aiModelVersion string = '2025-04-16' @description('Optional. AI model deployment token capacity. Lower this if initial provisioning fails due to capacity. Defaults to 50K tokens per minute to improve regional success rate.') param aiModelCapacity int = 500 +@minLength(1) +@description('Optional. Name of the embedding model to deploy. Defaults to text-embedding-3-large.') +param aiEmbeddingModelName string = 'text-embedding-3-large' + +@description('Optional. Version of the embedding model. Defaults to 1.') +param aiEmbeddingModelVersion string = '1' + +@description('Optional. Embedding model deployment token capacity. Defaults to 500.') +param aiEmbeddingModelCapacity int = 500 + @description('Optional. The tags to apply to all deployed Azure resources.') param tags resourceInput<'Microsoft.Resources/resourceGroups@2025-04-01'>.tags = {} @@ -586,6 +596,7 @@ var cosmosDbHaLocation = cosmosDbZoneRedundantHaRegionPairs[resourceGroup().loca var cosmosDatabaseName = 'migration_db' var processCosmosContainerName = 'processes' var agentTelemetryCosmosContainerName = 'agent_telemetry' +var processControlCosmosContainerName = 'processcontrol' module cosmosDb 'br/public:avm/res/document-db/database-account:0.15.0' = { name: take('avm.res.document-db.database-account.${cosmosDbResourceName}', 64) params: { @@ -609,6 +620,12 @@ module cosmosDb 'br/public:avm/res/document-db/database-account:0.15.0' = { '/_partitionKey' ] } + { + name: processControlCosmosContainerName + paths: [ + '/_partitionKey' + ] + } { name: 'files' paths: [ @@ -754,6 +771,18 @@ module existingAiFoundryAiServicesDeployments 'modules/ai-services-deployments.b capacity: aiModelCapacity } } + { + name: aiEmbeddingModelName + model: { + format: 'OpenAI' + name: aiEmbeddingModelName + version: aiEmbeddingModelVersion + } + sku: { + name: 'Standard' + capacity: aiEmbeddingModelCapacity + } + } ] roleAssignments: [ // Service Principal permissions @@ -850,6 +879,18 @@ module aiFoundry 'br/public:avm/ptn/ai-ml/ai-foundry:0.4.0' = if(!useExistingAiF capacity: aiModelCapacity } } + { + name: aiEmbeddingModelName + model: { + format: 'OpenAI' + name: aiEmbeddingModelName + version: aiEmbeddingModelVersion + } + sku: { + name: 'Standard' + capacity: aiEmbeddingModelCapacity + } + } ] tags: allTags enableTelemetry: enableTelemetry @@ -892,12 +933,16 @@ module appConfiguration 'br/public:avm/res/app-configuration/configuration-store } { name: 'AZURE_OPENAI_API_VERSION' - value: '2025-01-01-preview' + value: '2025-03-01-preview' } { name: 'AZURE_OPENAI_CHAT_DEPLOYMENT_NAME' value: aiModelDeploymentName } + { + name: 'AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME' + value: aiEmbeddingModelName + } { name: 'AZURE_OPENAI_ENDPOINT' value: 'https://${aiServicesName}.cognitiveservices.azure.com/' @@ -934,6 +979,11 @@ module appConfiguration 'br/public:avm/res/app-configuration/configuration-store name: 'COSMOS_DB_CONTAINER_NAME' value: agentTelemetryCosmosContainerName } + { + name: 'COSMOS_DB_CONTROL_CONTAINER_NAME' + value: processControlCosmosContainerName + } + { name: 'COSMOS_DB_DATABASE_NAME' value: cosmosDatabaseName diff --git a/infra/main_custom.bicep b/infra/main_custom.bicep index f8ca201..ee99c5b 100644 --- a/infra/main_custom.bicep +++ b/infra/main_custom.bicep @@ -32,11 +32,11 @@ var solutionLocation = empty(location) ? resourceGroup().location : location azd: { type: 'location' usageName: [ - 'OpenAI.GlobalStandard.o3, 500' + 'OpenAI.GlobalStandard.GPT5.1, 500' ] } }) -@description('Required. Azure region for AI services (OpenAI/AI Foundry). Must be a region that supports o3 model deployment.') +@description('Required. Azure region for AI services (OpenAI/AI Foundry). Must be a region that supports GPT5.1 model deployment.') param azureAiServiceLocation string @@ -61,8 +61,8 @@ param frontendImageName string = '' param aiDeploymentType string = 'GlobalStandard' @minLength(1) -@description('Optional. Name of the AI model to deploy. Recommend using o3. Defaults to o3.') -param aiModelName string = 'o3' +@description('Optional. Name of the AI model to deploy. Recommend using GPT5.1. Defaults to GPT5.1.') +param aiModelName string = 'GPT5.1' @minLength(1) @description('Optional. Version of AI model. Review available version numbers per model before setting. Defaults to 2025-04-16.') @@ -877,7 +877,7 @@ module appConfiguration 'br/public:avm/res/app-configuration/configuration-store } { name: 'AZURE_OPENAI_API_VERSION' - value: '2025-01-01-preview' + value: '2025-03-01-preview' } { name: 'AZURE_OPENAI_CHAT_DEPLOYMENT_NAME' diff --git a/scripts/checkquota.sh b/scripts/checkquota.sh index 532d179..f41c71d 100644 --- a/scripts/checkquota.sh +++ b/scripts/checkquota.sh @@ -31,7 +31,7 @@ echo "✅ Azure subscription set successfully." # Define models and their minimum required capacities declare -A MIN_CAPACITY=( - ["OpenAI.GlobalStandard.o3"]=$GPT_MIN_CAPACITY + ["OpenAI.GlobalStandard.gpt-5.1"]=$GPT_MIN_CAPACITY ) VALID_REGION="" diff --git a/scripts/quota_check_params.sh b/scripts/quota_check_params.sh index c57641e..c938218 100644 --- a/scripts/quota_check_params.sh +++ b/scripts/quota_check_params.sh @@ -47,7 +47,7 @@ log_verbose() { } # Default Models and Capacities (Comma-separated in "model:capacity" format) -DEFAULT_MODEL_CAPACITY="o3:500" +DEFAULT_MODEL_CAPACITY="gpt-5.1:500" # Convert the comma-separated string into an array IFS=',' read -r -a MODEL_CAPACITY_PAIRS <<< "$DEFAULT_MODEL_CAPACITY" diff --git a/src/backend-api/src/app/libs/application/application_configuration.py b/src/backend-api/src/app/libs/application/application_configuration.py index bb8d9b8..e9279c5 100644 --- a/src/backend-api/src/app/libs/application/application_configuration.py +++ b/src/backend-api/src/app/libs/application/application_configuration.py @@ -34,8 +34,12 @@ class Configuration(_configuration_base, KernelBaseSettings): app_sample_variable: str = Field(default="Hello World!") # Azure logging configuration - azure_package_logging_level: str = Field(default="WARNING", alias="AZURE_PACKAGE_LOGGING_LEVEL") - azure_logging_packages: str | None = Field(default=None, alias="AZURE_LOGGING_PACKAGES") + azure_package_logging_level: str = Field( + default="WARNING", alias="AZURE_PACKAGE_LOGGING_LEVEL" + ) + azure_logging_packages: str | None = Field( + default=None, alias="AZURE_LOGGING_PACKAGES" + ) global_llm_service: str | None = "AzureOpenAI" cosmos_db_process_log_container: str | None = Field( diff --git a/src/backend-api/src/app/libs/base/application_base.py b/src/backend-api/src/app/libs/base/application_base.py index 7b265c0..f726091 100644 --- a/src/backend-api/src/app/libs/base/application_base.py +++ b/src/backend-api/src/app/libs/base/application_base.py @@ -62,8 +62,20 @@ def __init__(self, env_file_path: str | None = None, **data): # Configure Azure package logging levels only if packages are specified if self.application_context.configuration.azure_logging_packages: - azure_level = getattr(logging, self.application_context.configuration.azure_package_logging_level.upper(), logging.WARNING) - for logger_name in filter(None, (pkg.strip() for pkg in self.application_context.configuration.azure_logging_packages.split(','))): + azure_level = getattr( + logging, + self.application_context.configuration.azure_package_logging_level.upper(), + logging.WARNING, + ) + for logger_name in filter( + None, + ( + pkg.strip() + for pkg in self.application_context.configuration.azure_logging_packages.split( + "," + ) + ), + ): logging.getLogger(logger_name).setLevel(azure_level) # Initialize the application diff --git a/src/backend-api/src/app/libs/base/fastapi_protocol.py b/src/backend-api/src/app/libs/base/fastapi_protocol.py index 763b6a4..cf9622d 100644 --- a/src/backend-api/src/app/libs/base/fastapi_protocol.py +++ b/src/backend-api/src/app/libs/base/fastapi_protocol.py @@ -16,8 +16,7 @@ class FastAPIWithContext(Protocol): app_context: AppContext # Include essential FastAPI methods for type checking - def include_router(self, *args, **kwargs) -> None: - ... + def include_router(self, *args, **kwargs) -> None: ... def add_app_context_to_fastapi( diff --git a/src/backend-api/src/app/libs/repositories/process_status_repository.py b/src/backend-api/src/app/libs/repositories/process_status_repository.py index b9215ab..7e61844 100644 --- a/src/backend-api/src/app/libs/repositories/process_status_repository.py +++ b/src/backend-api/src/app/libs/repositories/process_status_repository.py @@ -1,15 +1,13 @@ import asyncio +from datetime import UTC, datetime from typing import Any -from sas.cosmosdb.sql.repository import RepositoryBase - from routers.models.process_agent_activities import ( AgentStatus, ProcessStatus, ProcessStatusSnapshot, ) - -from datetime import datetime, UTC +from sas.cosmosdb.sql.repository import RepositoryBase def calculate_activity_duration(activity_start: str) -> tuple[int, str]: @@ -142,7 +140,7 @@ async def get_process_status_by_process_id( ) status = await self.get_async(process_id) - if status != None: + if status is not None: return ProcessStatusSnapshot( process_id=status.id, # Fix: use process_id instead of id step=status.step, @@ -223,6 +221,7 @@ async def render_agent_status(self, process_id: str) -> dict: "name": agent.name, "current_action": agent.current_action, "last_message_preview": agent.last_message_preview, + "last_full_message": getattr(agent, "last_full_message", ""), "last_update_time": agent.last_update_time, "is_active": agent.is_active, "is_currently_speaking": agent.is_currently_speaking, @@ -305,26 +304,7 @@ async def render_agent_status(self, process_id: str) -> dict: formatted_lines = [] agent_metrics = {} - # Check if process failed early (before agents really started working) process_failed = getattr(process_data, "status", "") == "failed" - process_duration_seconds = 0 - if hasattr(process_data, "started_at_time") and hasattr( - process_data, "last_update_time" - ): - try: - start = datetime.fromisoformat( - process_data.started_at_time.replace(" UTC", "+00:00") - ) - end = datetime.fromisoformat( - process_data.last_update_time.replace(" UTC", "+00:00") - ) - process_duration_seconds = int((end - start).total_seconds()) - except Exception: - pass - - early_failure = ( - process_failed and process_duration_seconds < 30 - ) # Failed in less than 30 seconds # Analyze each agent with enhanced insights for agent_name, agent_data in agents_data.items(): @@ -413,6 +393,23 @@ async def render_agent_status(self, process_id: str) -> dict: if is_active and duration_seconds > 30: message_parts.append(f"({duration_str})") + # Add tool usage from recent activity history + recent_tools = [] + for h in activity_history[-5:]: + tool = h.get("tool_used", "") + if tool and tool not in recent_tools: + recent_tools.append(tool) + if recent_tools: + tool_names = ", ".join( + recent_tools[-3:] + ) # Show last 3 unique tools + message_parts.append(f"🔧 {tool_names}") + + # Add activity count + total_activities = len(activity_history) + if total_activities > 0: + message_parts.append(f"📊 {total_activities} actions") + # Add relationship indicators if relationships["waiting_for"]: waiting_names = [ @@ -496,6 +493,25 @@ async def render_agent_status(self, process_id: str) -> dict: "bottleneck_score": total_blocking, "fast_agents": fast_agents, "failed_agents": failed_agents, + # NEW: Structured agent activities for rich frontend display + "agent_activities": agents_data, + # NEW: Step timing data + "step_timings": { + k: v + for k, v in ( + getattr(process_data, "step_timings", {}) or {} + ).items() + }, + # NEW: Step results and metrics + "step_results": { + k: v + for k, v in ( + getattr(process_data, "step_results", {}) or {} + ).items() + }, + "generated_files": getattr(process_data, "generated_files", []) or [], + "conversion_metrics": getattr(process_data, "conversion_metrics", {}) + or {}, } async def render_agent_status_old(self, process_id: str) -> dict: diff --git a/src/backend-api/src/app/libs/services/interfaces.py b/src/backend-api/src/app/libs/services/interfaces.py index d00b688..a061693 100644 --- a/src/backend-api/src/app/libs/services/interfaces.py +++ b/src/backend-api/src/app/libs/services/interfaces.py @@ -2,7 +2,7 @@ # Licensed under the MIT License. from abc import ABC, abstractmethod -from typing import Any, Dict, Union +from typing import Any, Dict class IDataService(ABC): diff --git a/src/backend-api/src/app/routers/models/files.py b/src/backend-api/src/app/routers/models/files.py index 076083e..58a26be 100644 --- a/src/backend-api/src/app/routers/models/files.py +++ b/src/backend-api/src/app/routers/models/files.py @@ -1,4 +1,3 @@ -from datetime import datetime, timezone from pydantic import BaseModel, Field diff --git a/src/backend-api/src/app/routers/router_files.py b/src/backend-api/src/app/routers/router_files.py index c43179b..aa778f7 100644 --- a/src/backend-api/src/app/routers/router_files.py +++ b/src/backend-api/src/app/routers/router_files.py @@ -13,17 +13,14 @@ ) from fastapi.responses import Response from libs.base.typed_fastapi import TypedFastAPI -from libs.models.entities import File, Process +from libs.models.entities import File from libs.sas.storage import AsyncStorageBlobHelper from libs.services.auth import get_authenticated_user from libs.services.input_validation import is_valid_uuid from libs.services.interfaces import ILoggerService -from libs.sas.storage import AsyncStorageBlobHelper -from libs.models.entities import File, Process from routers.models.files import FileUploadResult from libs.repositories.process_repository import ProcessRepository from libs.repositories.file_repository import FileRepository -from libs.repositories.process_repository import ProcessRepository router = APIRouter( prefix="/api/file", diff --git a/src/frontend/package-lock.json b/src/frontend/package-lock.json index 07d3890..7e0fce0 100644 --- a/src/frontend/package-lock.json +++ b/src/frontend/package-lock.json @@ -22,15 +22,22 @@ "axios": "^1.7.9", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", - "lucide-react": "^0.471.1", + "js-yaml": "^4.1.0", + "lottie-react": "^2.4.1", + "lucide-react": "^0.471.2", + "mermaid": "^11.13.0", + "messagebar": "^0.2.1", "postcss": "^8.5.0", "react": "^18.3.1", "react-dom": "^18.3.1", "react-dropzone": "^14.3.5", "react-icons": "^5.5.0", + "react-markdown": "^10.1.0", "react-redux": "^9.1.2", "react-router-dom": "^7.1.3", "react-syntax-highlighter": "^15.6.1", + "rehype-raw": "^7.0.0", + "remark-gfm": "^4.0.1", "sql-formatter": "^15.4.11", "tailwind-merge": "^2.6.0", "tailwindcss": "^3.4.17", @@ -78,6 +85,18 @@ "node": ">=6.0.0" } }, + "node_modules/@antfu/install-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-1.1.0.tgz", + "integrity": "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==", + "dependencies": { + "package-manager-detector": "^1.3.0", + "tinyexec": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/@azure/msal-browser": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-4.7.0.tgz", @@ -403,6 +422,45 @@ "node": ">=6.9.0" } }, + "node_modules/@braintree/sanitize-url": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-7.1.2.tgz", + "integrity": "sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA==" + }, + "node_modules/@chevrotain/cst-dts-gen": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-11.1.2.tgz", + "integrity": "sha512-XTsjvDVB5nDZBQB8o0o/0ozNelQtn2KrUVteIHSlPd2VAV2utEb6JzyCJaJ8tGxACR4RiBNWy5uYUHX2eji88Q==", + "dependencies": { + "@chevrotain/gast": "11.1.2", + "@chevrotain/types": "11.1.2", + "lodash-es": "4.17.23" + } + }, + "node_modules/@chevrotain/gast": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-11.1.2.tgz", + "integrity": "sha512-Z9zfXR5jNZb1Hlsd/p+4XWeUFugrHirq36bKzPWDSIacV+GPSVXdk+ahVWZTwjhNwofAWg/sZg58fyucKSQx5g==", + "dependencies": { + "@chevrotain/types": "11.1.2", + "lodash-es": "4.17.23" + } + }, + "node_modules/@chevrotain/regexp-to-ast": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/@chevrotain/regexp-to-ast/-/regexp-to-ast-11.1.2.tgz", + "integrity": "sha512-nMU3Uj8naWer7xpZTYJdxbAs6RIv/dxYzkYU8GSwgUtcAAlzjcPfX1w+RKRcYG8POlzMeayOQ/znfwxEGo5ulw==" + }, + "node_modules/@chevrotain/types": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-11.1.2.tgz", + "integrity": "sha512-U+HFai5+zmJCkK86QsaJtoITlboZHBqrVketcO2ROv865xfCMSFpELQoz1GkX5GzME8pTa+3kbKrZHQtI0gdbw==" + }, + "node_modules/@chevrotain/utils": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-11.1.2.tgz", + "integrity": "sha512-4mudFAQ6H+MqBTfqLmU7G1ZwRzCLfJEooL/fsF6rCX5eePMbGhoy5n4g+G4vlh2muDcsCTJtL+uKbOzWxs5LHA==" + }, "node_modules/@emotion/hash": { "version": "0.9.2", "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", @@ -2793,6 +2851,21 @@ "url": "https://github.com/sponsors/nzakas" } }, + "node_modules/@iconify/types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", + "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==" + }, + "node_modules/@iconify/utils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-3.1.0.tgz", + "integrity": "sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw==", + "dependencies": { + "@antfu/install-pkg": "^1.1.0", + "@iconify/types": "^2.0.0", + "mlly": "^1.8.0" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -2858,6 +2931,14 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@mermaid-js/parser": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-1.0.1.tgz", + "integrity": "sha512-opmV19kN1JsK0T6HhhokHpcVkqKpF+x2pPDKKM2ThHtZAB5F4PROopk0amuVYK5qMrIA4erzpNm8gmPNJgMDxQ==", + "dependencies": { + "langium": "^4.0.0" + } + }, "node_modules/@microsoft/load-themed-styles": { "version": "1.10.295", "resolved": "https://registry.npmjs.org/@microsoft/load-themed-styles/-/load-themed-styles-1.10.295.tgz", @@ -3746,12 +3827,255 @@ "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", "license": "MIT" }, + "node_modules/@types/d3": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz", + "integrity": "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==", + "dependencies": { + "@types/d3-array": "*", + "@types/d3-axis": "*", + "@types/d3-brush": "*", + "@types/d3-chord": "*", + "@types/d3-color": "*", + "@types/d3-contour": "*", + "@types/d3-delaunay": "*", + "@types/d3-dispatch": "*", + "@types/d3-drag": "*", + "@types/d3-dsv": "*", + "@types/d3-ease": "*", + "@types/d3-fetch": "*", + "@types/d3-force": "*", + "@types/d3-format": "*", + "@types/d3-geo": "*", + "@types/d3-hierarchy": "*", + "@types/d3-interpolate": "*", + "@types/d3-path": "*", + "@types/d3-polygon": "*", + "@types/d3-quadtree": "*", + "@types/d3-random": "*", + "@types/d3-scale": "*", + "@types/d3-scale-chromatic": "*", + "@types/d3-selection": "*", + "@types/d3-shape": "*", + "@types/d3-time": "*", + "@types/d3-time-format": "*", + "@types/d3-timer": "*", + "@types/d3-transition": "*", + "@types/d3-zoom": "*" + } + }, + "node_modules/@types/d3-array": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz", + "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==" + }, + "node_modules/@types/d3-axis": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.6.tgz", + "integrity": "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-brush": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.6.tgz", + "integrity": "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-chord": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.6.tgz", + "integrity": "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==" + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==" + }, + "node_modules/@types/d3-contour": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.6.tgz", + "integrity": "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==", + "dependencies": { + "@types/d3-array": "*", + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==" + }, + "node_modules/@types/d3-dispatch": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.7.tgz", + "integrity": "sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==" + }, + "node_modules/@types/d3-drag": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz", + "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-dsv": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.7.tgz", + "integrity": "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==" + }, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==" + }, + "node_modules/@types/d3-fetch": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.7.tgz", + "integrity": "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==", + "dependencies": { + "@types/d3-dsv": "*" + } + }, + "node_modules/@types/d3-force": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.10.tgz", + "integrity": "sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==" + }, + "node_modules/@types/d3-format": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.4.tgz", + "integrity": "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==" + }, + "node_modules/@types/d3-geo": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.1.0.tgz", + "integrity": "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==", + "dependencies": { + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-hierarchy": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz", + "integrity": "sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", + "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==" + }, + "node_modules/@types/d3-polygon": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.2.tgz", + "integrity": "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==" + }, + "node_modules/@types/d3-quadtree": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz", + "integrity": "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==" + }, + "node_modules/@types/d3-random": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.3.tgz", + "integrity": "sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", + "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==" + }, + "node_modules/@types/d3-selection": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.11.tgz", + "integrity": "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==" + }, + "node_modules/@types/d3-shape": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.8.tgz", + "integrity": "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", + "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==" + }, + "node_modules/@types/d3-time-format": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.3.tgz", + "integrity": "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==" + }, + "node_modules/@types/d3-transition": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.9.tgz", + "integrity": "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-zoom": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz", + "integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==", + "dependencies": { + "@types/d3-interpolate": "*", + "@types/d3-selection": "*" + } + }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "dependencies": { + "@types/ms": "*" + } + }, "node_modules/@types/estree": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", "license": "MIT" }, + "node_modules/@types/estree-jsx": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", + "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/@types/geojson": { + "version": "7946.0.16", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", + "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==" + }, "node_modules/@types/hast": { "version": "2.3.10", "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.10.tgz", @@ -3774,6 +4098,19 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==" + }, "node_modules/@types/prop-types": { "version": "15.7.14", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", @@ -3820,6 +4157,12 @@ "@types/react-router": "*" } }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "optional": true + }, "node_modules/@types/unist": { "version": "2.0.11", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", @@ -3832,6 +4175,20 @@ "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==", "license": "MIT" }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==" + }, + "node_modules/@upsetjs/venn.js": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@upsetjs/venn.js/-/venn.js-2.0.0.tgz", + "integrity": "sha512-WbBhLrooyePuQ1VZxrJjtLvTc4NVfpOyKx0sKqioq9bX1C1m7Jgykkn8gLrtwumBioXIqam8DLxp88Adbue6Hw==", + "optionalDependencies": { + "d3-selection": "^3.0.0", + "d3-transition": "^3.0.1" + } + }, "node_modules/@vitejs/plugin-react": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.4.tgz", @@ -3853,11 +4210,9 @@ } }, "node_modules/acorn": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", - "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", - "dev": true, - "license": "MIT", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "bin": { "acorn": "bin/acorn" }, @@ -4175,6 +4530,15 @@ "proxy-from-env": "^1.1.0" } }, + "node_modules/bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -4350,6 +4714,15 @@ ], "license": "CC-BY-4.0" }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -4377,6 +4750,15 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/character-entities-legacy": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz", @@ -4397,6 +4779,30 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/chevrotain": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-11.1.2.tgz", + "integrity": "sha512-opLQzEVriiH1uUQ4Kctsd49bRoFDXGGSC4GUqj7pGyxM3RehRhvTlZJc1FL/Flew2p5uwxa1tUDWKzI4wNM8pg==", + "dependencies": { + "@chevrotain/cst-dts-gen": "11.1.2", + "@chevrotain/gast": "11.1.2", + "@chevrotain/regexp-to-ast": "11.1.2", + "@chevrotain/types": "11.1.2", + "@chevrotain/utils": "11.1.2", + "lodash-es": "4.17.23" + } + }, + "node_modules/chevrotain-allstar": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/chevrotain-allstar/-/chevrotain-allstar-0.3.1.tgz", + "integrity": "sha512-b7g+y9A0v4mxCW1qUhf3BSVPg+/NvGErk/dOkrDaHA0nQIQGAtrOjlX//9OQtRlSCy+x9rfB5N8yC71lH1nvMw==", + "dependencies": { + "lodash-es": "^4.17.21" + }, + "peerDependencies": { + "chevrotain": "^11.0.0" + } + }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -4510,6 +4916,11 @@ "dev": true, "license": "MIT" }, + "node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==" + }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -4526,6 +4937,14 @@ "node": ">=18" } }, + "node_modules/cose-base": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-1.0.3.tgz", + "integrity": "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==", + "dependencies": { + "layout-base": "^1.0.0" + } + }, "node_modules/cosmiconfig": { "version": "8.3.6", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", @@ -4585,1456 +5004,1536 @@ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", "license": "MIT" }, - "node_modules/data-view-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", - "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.2" - }, + "node_modules/cytoscape": { + "version": "3.33.1", + "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.33.1.tgz", + "integrity": "sha512-iJc4TwyANnOGR1OmWhsS9ayRS3s+XQ185FmuHObThD+5AeJCakAAbWv8KimMTt08xCCLNgneQwFp+JRJOr9qGQ==", "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.10" } }, - "node_modules/data-view-byte-length": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", - "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", - "dev": true, - "license": "MIT", + "node_modules/cytoscape-cose-bilkent": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz", + "integrity": "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==", "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.2" + "cose-base": "^1.0.0" }, - "engines": { - "node": ">= 0.4" + "peerDependencies": { + "cytoscape": "^3.2.0" + } + }, + "node_modules/cytoscape-fcose": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cytoscape-fcose/-/cytoscape-fcose-2.2.0.tgz", + "integrity": "sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==", + "dependencies": { + "cose-base": "^2.2.0" }, - "funding": { - "url": "https://github.com/sponsors/inspect-js" + "peerDependencies": { + "cytoscape": "^3.2.0" } }, - "node_modules/data-view-byte-offset": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", - "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", - "dev": true, - "license": "MIT", + "node_modules/cytoscape-fcose/node_modules/cose-base": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-2.2.0.tgz", + "integrity": "sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==", "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" + "layout-base": "^2.0.0" + } + }, + "node_modules/cytoscape-fcose/node_modules/layout-base": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-2.0.1.tgz", + "integrity": "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==" + }, + "node_modules/d3": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz", + "integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==", + "dependencies": { + "d3-array": "3", + "d3-axis": "3", + "d3-brush": "3", + "d3-chord": "3", + "d3-color": "3", + "d3-contour": "4", + "d3-delaunay": "6", + "d3-dispatch": "3", + "d3-drag": "3", + "d3-dsv": "3", + "d3-ease": "3", + "d3-fetch": "3", + "d3-force": "3", + "d3-format": "3", + "d3-geo": "3", + "d3-hierarchy": "3", + "d3-interpolate": "3", + "d3-path": "3", + "d3-polygon": "3", + "d3-quadtree": "3", + "d3-random": "3", + "d3-scale": "4", + "d3-scale-chromatic": "3", + "d3-selection": "3", + "d3-shape": "3", + "d3-time": "3", + "d3-time-format": "4", + "d3-timer": "3", + "d3-transition": "3", + "d3-zoom": "3" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=12" } }, - "node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "dev": true, - "license": "MIT", + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", "dependencies": { - "ms": "^2.1.3" + "internmap": "1 - 2" }, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">=12" } }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true, - "license": "MIT" + "node_modules/d3-axis": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", + "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==", + "engines": { + "node": ">=12" + } }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "dev": true, - "license": "MIT", + "node_modules/d3-brush": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", + "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "3", + "d3-transition": "3" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=12" } }, - "node_modules/define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "dev": true, - "license": "MIT", + "node_modules/d3-chord": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", + "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" + "d3-path": "1 - 3" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=12" } }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "license": "MIT", + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", "engines": { - "node": ">=0.4.0" + "node": ">=12" } }, - "node_modules/detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", - "license": "Apache-2.0", - "bin": { - "detect-libc": "bin/detect-libc.js" + "node_modules/d3-contour": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz", + "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==", + "dependencies": { + "d3-array": "^3.2.0" }, "engines": { - "node": ">=0.10" + "node": ">=12" } }, - "node_modules/didyoumean": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", - "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", - "license": "Apache-2.0" - }, - "node_modules/discontinuous-range": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz", - "integrity": "sha512-c68LpLbO+7kP/b1Hr1qs8/BJ09F5khZGTxqxZuhzxpmwJKOgRFHJWIb9/KmqnqHhLdO55aOxFH/EGBvUQbL/RQ==", - "license": "MIT" - }, - "node_modules/dlv": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", - "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", - "license": "MIT" - }, - "node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "license": "Apache-2.0", + "node_modules/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==", "dependencies": { - "esutils": "^2.0.2" + "delaunator": "5" }, "engines": { - "node": ">=0.10.0" + "node": ">=12" } }, - "node_modules/dot-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", - "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", - "dev": true, - "license": "MIT", - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "engines": { + "node": ">=12" } }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "dev": true, - "license": "MIT", + "node_modules/d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" + "d3-dispatch": "1 - 3", + "d3-selection": "3" }, "engines": { - "node": ">= 0.4" + "node": ">=12" } }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "license": "MIT" - }, - "node_modules/electron-to-chromium": { - "version": "1.5.92", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.92.tgz", - "integrity": "sha512-BeHgmNobs05N1HMmMZ7YIuHfYBGlq/UmvlsTgg+fsbFs9xVMj+xJHFg19GN04+9Q+r8Xnh9LXqaYIyEWElnNgQ==", - "license": "ISC" - }, - "node_modules/embla-carousel": { - "version": "8.5.2", - "resolved": "https://registry.npmjs.org/embla-carousel/-/embla-carousel-8.5.2.tgz", - "integrity": "sha512-xQ9oVLrun/eCG/7ru3R+I5bJ7shsD8fFwLEY7yPe27/+fDHCNj0OT5EoG5ZbFyOxOcG6yTwW8oTz/dWyFnyGpg==", - "license": "MIT" + "node_modules/d3-dsv": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", + "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", + "dependencies": { + "commander": "7", + "iconv-lite": "0.6", + "rw": "1" + }, + "bin": { + "csv2json": "bin/dsv2json.js", + "csv2tsv": "bin/dsv2dsv.js", + "dsv2dsv": "bin/dsv2dsv.js", + "dsv2json": "bin/dsv2json.js", + "json2csv": "bin/json2dsv.js", + "json2dsv": "bin/json2dsv.js", + "json2tsv": "bin/json2dsv.js", + "tsv2csv": "bin/dsv2dsv.js", + "tsv2json": "bin/dsv2json.js" + }, + "engines": { + "node": ">=12" + } }, - "node_modules/embla-carousel-autoplay": { - "version": "8.5.2", - "resolved": "https://registry.npmjs.org/embla-carousel-autoplay/-/embla-carousel-autoplay-8.5.2.tgz", - "integrity": "sha512-27emJ0px3q/c0kCHCjwRrEbYcyYUPfGO3g5IBWF1i7714TTzE6L9P81V6PHLoSMAKJ1aHoT2e7YFOsuFKCbyag==", - "license": "MIT", - "peerDependencies": { - "embla-carousel": "8.5.2" + "node_modules/d3-dsv/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "engines": { + "node": ">= 10" } }, - "node_modules/embla-carousel-fade": { - "version": "8.5.2", - "resolved": "https://registry.npmjs.org/embla-carousel-fade/-/embla-carousel-fade-8.5.2.tgz", - "integrity": "sha512-QJ46Xy+mpijjquQeIY0d0sPSy34XduREUnz7tn1K20hcKyZYTONNIXQZu3GGNwG59cvhMqYJMw9ki92Rjd14YA==", - "license": "MIT", - "peerDependencies": { - "embla-carousel": "8.5.2" + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "engines": { + "node": ">=12" } }, - "node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "license": "MIT" + "node_modules/d3-fetch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", + "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", + "dependencies": { + "d3-dsv": "1 - 3" + }, + "engines": { + "node": ">=12" + } }, - "node_modules/enhanced-resolve": { - "version": "5.18.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", - "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==", - "license": "MIT", + "node_modules/d3-force": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", + "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" + "d3-dispatch": "1 - 3", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" }, "engines": { - "node": ">=10.13.0" + "node": ">=12" } }, - "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "dev": true, - "license": "BSD-2-Clause", + "node_modules/d3-format": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.2.tgz", + "integrity": "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==", "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" + "node": ">=12" } }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "license": "MIT", + "node_modules/d3-geo": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz", + "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==", "dependencies": { - "is-arrayish": "^0.2.1" + "d3-array": "2.5.0 - 3" + }, + "engines": { + "node": ">=12" } }, - "node_modules/es-abstract": { - "version": "1.23.9", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz", - "integrity": "sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==", - "dev": true, - "license": "MIT", + "node_modules/d3-hierarchy": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", + "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", "dependencies": { - "array-buffer-byte-length": "^1.0.2", - "arraybuffer.prototype.slice": "^1.0.4", - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "data-view-buffer": "^1.0.2", - "data-view-byte-length": "^1.0.2", - "data-view-byte-offset": "^1.0.1", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-set-tostringtag": "^2.1.0", - "es-to-primitive": "^1.3.0", - "function.prototype.name": "^1.1.8", - "get-intrinsic": "^1.2.7", - "get-proto": "^1.0.0", - "get-symbol-description": "^1.1.0", - "globalthis": "^1.0.4", - "gopd": "^1.2.0", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "internal-slot": "^1.1.0", - "is-array-buffer": "^3.0.5", - "is-callable": "^1.2.7", - "is-data-view": "^1.0.2", - "is-regex": "^1.2.1", - "is-shared-array-buffer": "^1.0.4", - "is-string": "^1.1.1", - "is-typed-array": "^1.1.15", - "is-weakref": "^1.1.0", - "math-intrinsics": "^1.1.0", - "object-inspect": "^1.13.3", - "object-keys": "^1.1.1", - "object.assign": "^4.1.7", - "own-keys": "^1.0.1", - "regexp.prototype.flags": "^1.5.3", - "safe-array-concat": "^1.1.3", - "safe-push-apply": "^1.0.0", - "safe-regex-test": "^1.1.0", - "set-proto": "^1.0.0", - "string.prototype.trim": "^1.2.10", - "string.prototype.trimend": "^1.0.9", - "string.prototype.trimstart": "^1.0.8", - "typed-array-buffer": "^1.0.3", - "typed-array-byte-length": "^1.0.3", - "typed-array-byte-offset": "^1.0.4", - "typed-array-length": "^1.0.7", - "unbox-primitive": "^1.1.0", - "which-typed-array": "^1.1.18" + "d3-color": "1 - 3" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=12" } }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "dev": true, - "license": "MIT", + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", "engines": { - "node": ">= 0.4" + "node": ">=12" } }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, - "license": "MIT", + "node_modules/d3-polygon": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", + "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==", "engines": { - "node": ">= 0.4" + "node": ">=12" } }, - "node_modules/es-iterator-helpers": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.1.tgz", - "integrity": "sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.6", - "es-errors": "^1.3.0", - "es-set-tostringtag": "^2.0.3", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.6", - "globalthis": "^1.0.4", - "gopd": "^1.2.0", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.2.0", - "has-symbols": "^1.1.0", - "internal-slot": "^1.1.0", - "iterator.prototype": "^1.1.4", - "safe-array-concat": "^1.1.3" - }, + "node_modules/d3-quadtree": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", + "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", "engines": { - "node": ">= 0.4" + "node": ">=12" } }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "dev": true, - "license": "MIT", + "node_modules/d3-random": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", + "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-sankey": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/d3-sankey/-/d3-sankey-0.12.3.tgz", + "integrity": "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==", "dependencies": { - "es-errors": "^1.3.0" + "d3-array": "1 - 2", + "d3-shape": "^1.2.0" + } + }, + "node_modules/d3-sankey/node_modules/d3-array": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", + "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==", + "dependencies": { + "internmap": "^1.0.0" + } + }, + "node_modules/d3-sankey/node_modules/d3-path": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", + "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==" + }, + "node_modules/d3-sankey/node_modules/d3-shape": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", + "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", + "dependencies": { + "d3-path": "1" + } + }, + "node_modules/d3-sankey/node_modules/internmap": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz", + "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==" + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" }, "engines": { - "node": ">= 0.4" + "node": ">=12" } }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "dev": true, - "license": "MIT", + "node_modules/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==", "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" + "d3-color": "1 - 3", + "d3-interpolate": "1 - 3" }, "engines": { - "node": ">= 0.4" + "node": ">=12" } }, - "node_modules/es-shim-unscopables": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", - "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", - "dev": true, - "license": "MIT", + "node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", "dependencies": { - "hasown": "^2.0.0" + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" } }, - "node_modules/es-to-primitive": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", - "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", - "dev": true, - "license": "MIT", + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", "dependencies": { - "is-callable": "^1.2.7", - "is-date-object": "^1.0.5", - "is-symbol": "^1.0.4" + "d3-array": "2 - 3" }, "engines": { - "node": ">= 0.4" + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "dependencies": { + "d3-time": "1 - 3" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=12" } }, - "node_modules/esbuild": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.2.tgz", - "integrity": "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==", - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "dependencies": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" }, "engines": { - "node": ">=18" + "node": ">=12" }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.24.2", - "@esbuild/android-arm": "0.24.2", - "@esbuild/android-arm64": "0.24.2", - "@esbuild/android-x64": "0.24.2", - "@esbuild/darwin-arm64": "0.24.2", - "@esbuild/darwin-x64": "0.24.2", - "@esbuild/freebsd-arm64": "0.24.2", - "@esbuild/freebsd-x64": "0.24.2", - "@esbuild/linux-arm": "0.24.2", - "@esbuild/linux-arm64": "0.24.2", - "@esbuild/linux-ia32": "0.24.2", - "@esbuild/linux-loong64": "0.24.2", - "@esbuild/linux-mips64el": "0.24.2", - "@esbuild/linux-ppc64": "0.24.2", - "@esbuild/linux-riscv64": "0.24.2", - "@esbuild/linux-s390x": "0.24.2", - "@esbuild/linux-x64": "0.24.2", - "@esbuild/netbsd-arm64": "0.24.2", - "@esbuild/netbsd-x64": "0.24.2", - "@esbuild/openbsd-arm64": "0.24.2", - "@esbuild/openbsd-x64": "0.24.2", - "@esbuild/sunos-x64": "0.24.2", - "@esbuild/win32-arm64": "0.24.2", - "@esbuild/win32-ia32": "0.24.2", - "@esbuild/win32-x64": "0.24.2" + "peerDependencies": { + "d3-selection": "2 - 3" } }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "license": "MIT", + "node_modules/d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + }, "engines": { - "node": ">=6" + "node": ">=12" } }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "node_modules/dagre-d3-es": { + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.14.tgz", + "integrity": "sha512-P4rFMVq9ESWqmOgK+dlXvOtLwYg0i7u0HBGJER0LZDJT2VHIPAMZ/riPxqJceWMStH5+E61QxFra9kIS3AqdMg==", + "dependencies": { + "d3": "^7.9.0", + "lodash-es": "^4.17.21" + } + }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", "dev": true, "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/eslint": { - "version": "9.19.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.19.0.tgz", - "integrity": "sha512-ug92j0LepKlbbEv6hD911THhoRHmbdXt2gX+VDABAW/Ir7D3nqKdv5Pf5vtlyY6HQMTEP2skXY43ueqTCWssEA==", + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.19.0", - "@eslint/core": "^0.10.0", - "@eslint/eslintrc": "^3.2.0", - "@eslint/js": "9.19.0", - "@eslint/plugin-kit": "^0.2.5", - "@humanfs/node": "^0.16.6", - "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.1", - "@types/estree": "^1.0.6", - "@types/json-schema": "^7.0.15", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.6", - "debug": "^4.3.2", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.2.0", - "eslint-visitor-keys": "^4.2.0", - "espree": "^10.3.0", - "esquery": "^1.5.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^8.0.0", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3" + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" }, - "bin": { - "eslint": "bin/eslint.js" + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">= 0.4" }, "funding": { - "url": "https://eslint.org/donate" + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/dayjs": { + "version": "1.11.20", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.20.tgz", + "integrity": "sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ==" + }, + "node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" }, - "peerDependencies": { - "jiti": "*" + "engines": { + "node": ">=6.0" }, "peerDependenciesMeta": { - "jiti": { + "supports-color": { "optional": true } } }, - "node_modules/eslint-plugin-react": { - "version": "7.37.4", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.4.tgz", - "integrity": "sha512-BGP0jRmfYyvOyvMoRX/uoUeW+GqNj9y16bPQzqAHf3AYII/tDs+jMN0dBVkl88/OZwNGwrVFxE7riHsXVfy/LQ==", - "dev": true, - "license": "MIT", + "node_modules/decode-named-character-reference": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz", + "integrity": "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==", "dependencies": { - "array-includes": "^3.1.8", - "array.prototype.findlast": "^1.2.5", - "array.prototype.flatmap": "^1.3.3", - "array.prototype.tosorted": "^1.1.4", - "doctrine": "^2.1.0", - "es-iterator-helpers": "^1.2.1", - "estraverse": "^5.3.0", - "hasown": "^2.0.2", - "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "minimatch": "^3.1.2", - "object.entries": "^1.1.8", - "object.fromentries": "^2.0.8", - "object.values": "^1.2.1", - "prop-types": "^15.8.1", - "resolve": "^2.0.0-next.5", - "semver": "^6.3.1", - "string.prototype.matchall": "^4.0.12", - "string.prototype.repeat": "^1.0.0" - }, - "engines": { - "node": ">=4" + "character-entities": "^2.0.0" }, - "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/eslint-plugin-react-hooks": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.1.0.tgz", - "integrity": "sha512-mpJRtPgHN2tNAvZ35AMfqeB3Xqeo273QxrHJsbBEPWODRM4r0yB6jfoROqKEYrOn27UtRPpcpHc2UqyBSuUNTw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" + "node_modules/decode-named-character-reference/node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/eslint-plugin-react-refresh": { - "version": "0.4.18", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.18.tgz", - "integrity": "sha512-IRGEoFn3OKalm3hjfolEWGqoF/jPqeEYFp+C8B0WMzwGwBMvlRDQd06kghDhF0C61uJ6WfSDhEZE/sAQjduKgw==", + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true, - "license": "MIT", - "peerDependencies": { - "eslint": ">=8.40" - } + "license": "MIT" }, - "node_modules/eslint-scope": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz", - "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==", + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">= 0.4" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "dev": true, - "license": "Apache-2.0", + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">= 0.4" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/espree": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", - "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", - "dev": true, - "license": "BSD-2-Clause", + "node_modules/delaunator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz", + "integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==", "dependencies": { - "acorn": "^8.14.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.0" - }, + "robust-predicates": "^3.0.2" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": ">=0.4.0" } }, - "node_modules/esquery": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "estraverse": "^5.1.0" + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "license": "Apache-2.0", + "bin": { + "detect-libc": "bin/detect-libc.js" }, "engines": { "node": ">=0.10" } }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "license": "BSD-2-Clause", + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", "dependencies": { - "estraverse": "^5.2.0" + "dequal": "^2.0.0" }, - "engines": { - "node": ">=4.0" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "license": "Apache-2.0" }, - "node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "dev": true, + "node_modules/discontinuous-range": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz", + "integrity": "sha512-c68LpLbO+7kP/b1Hr1qs8/BJ09F5khZGTxqxZuhzxpmwJKOgRFHJWIb9/KmqnqHhLdO55aOxFH/EGBvUQbL/RQ==", "license": "MIT" }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "license": "MIT" + }, + "node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", "dev": true, - "license": "BSD-2-Clause", + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, "engines": { "node": ">=0.10.0" } }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-glob": { + "node_modules/dompurify": { "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.3.tgz", + "integrity": "sha512-Oj6pzI2+RqBfFG+qOaOLbFXLQ90ARpcGG6UePL82bJLtdsa6CYJD7nmiU8MW9nQNOtCHV3lZ/Bzq1X0QYbBZCA==", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "dev": true, "license": "MIT", "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - }, - "engines": { - "node": ">=8.6.0" + "no-case": "^3.0.4", + "tslib": "^2.0.3" } }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "license": "ISC", + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", "dependencies": { - "is-glob": "^4.0.1" + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" }, "engines": { - "node": ">= 6" + "node": ">= 0.4" } }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "license": "MIT" }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true, - "license": "MIT" + "node_modules/electron-to-chromium": { + "version": "1.5.92", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.92.tgz", + "integrity": "sha512-BeHgmNobs05N1HMmMZ7YIuHfYBGlq/UmvlsTgg+fsbFs9xVMj+xJHFg19GN04+9Q+r8Xnh9LXqaYIyEWElnNgQ==", + "license": "ISC" }, - "node_modules/fastq": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.0.tgz", - "integrity": "sha512-7SFSRCNjBQIZH/xZR3iy5iQYR8aGBE0h3VG6/cwlbrpdciNYBMotQav8c1XI3HjHH+NikUpP53nPdlZSdWmFzA==", - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" + "node_modules/embla-carousel": { + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/embla-carousel/-/embla-carousel-8.5.2.tgz", + "integrity": "sha512-xQ9oVLrun/eCG/7ru3R+I5bJ7shsD8fFwLEY7yPe27/+fDHCNj0OT5EoG5ZbFyOxOcG6yTwW8oTz/dWyFnyGpg==", + "license": "MIT" + }, + "node_modules/embla-carousel-autoplay": { + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/embla-carousel-autoplay/-/embla-carousel-autoplay-8.5.2.tgz", + "integrity": "sha512-27emJ0px3q/c0kCHCjwRrEbYcyYUPfGO3g5IBWF1i7714TTzE6L9P81V6PHLoSMAKJ1aHoT2e7YFOsuFKCbyag==", + "license": "MIT", + "peerDependencies": { + "embla-carousel": "8.5.2" } }, - "node_modules/fault": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/fault/-/fault-1.0.4.tgz", - "integrity": "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==", + "node_modules/embla-carousel-fade": { + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/embla-carousel-fade/-/embla-carousel-fade-8.5.2.tgz", + "integrity": "sha512-QJ46Xy+mpijjquQeIY0d0sPSy34XduREUnz7tn1K20hcKyZYTONNIXQZu3GGNwG59cvhMqYJMw9ki92Rjd14YA==", "license": "MIT", - "dependencies": { - "format": "^0.2.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "peerDependencies": { + "embla-carousel": "8.5.2" } }, - "node_modules/file-entry-cache": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", - "dev": true, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" + }, + "node_modules/enhanced-resolve": { + "version": "5.18.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", + "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==", "license": "MIT", "dependencies": { - "flat-cache": "^4.0.0" + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" }, "engines": { - "node": ">=16.0.0" + "node": ">=10.13.0" } }, - "node_modules/file-selector": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/file-selector/-/file-selector-2.1.2.tgz", - "integrity": "sha512-QgXo+mXTe8ljeqUFaX3QVHc5osSItJ/Km+xpocx0aSqWGMSCf6qYs/VnzZgS864Pjn5iceMRFigeAV7AfTlaig==", - "license": "MIT", - "dependencies": { - "tslib": "^2.7.0" - }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", "engines": { - "node": ">= 12" + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, "license": "MIT", "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" + "is-arrayish": "^0.2.1" } }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "node_modules/es-abstract": { + "version": "1.23.9", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz", + "integrity": "sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==", "dev": true, "license": "MIT", "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.0", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-regex": "^1.2.1", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.0", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.3", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.3", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.18" }, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/flat-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "dev": true, "license": "MIT", - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.4" - }, "engines": { - "node": ">=16" + "node": ">= 0.4" } }, - "node_modules/flatted": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", - "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "dev": true, - "license": "ISC" - }, - "node_modules/follow-redirects": { - "version": "1.15.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", - "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], "license": "MIT", "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } + "node": ">= 0.4" } }, - "node_modules/for-each": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.4.tgz", - "integrity": "sha512-kKaIINnFpzW6ffJNDjjyjrk21BkDx38c0xa/klsT8VzLCaMEefv4ZTacrcVR4DmgTeBra++jMDAfS/tS799YDw==", + "node_modules/es-iterator-helpers": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.1.tgz", + "integrity": "sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==", "dev": true, "license": "MIT", "dependencies": { - "is-callable": "^1.2.7" + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.6", + "es-errors": "^1.3.0", + "es-set-tostringtag": "^2.0.3", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.6", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "iterator.prototype": "^1.1.4", + "safe-array-concat": "^1.1.3" }, "engines": { "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/foreground-child": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", - "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", - "license": "ISC", + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", "dependencies": { - "cross-spawn": "^7.0.0", - "signal-exit": "^4.0.1" + "es-errors": "^1.3.0" }, "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">= 0.4" } }, - "node_modules/form-data": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", - "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, "license": "MIT", "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" }, "engines": { - "node": ">= 6" - } - }, - "node_modules/format": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", - "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==", - "engines": { - "node": ">=0.4.x" + "node": ">= 0.4" } }, - "node_modules/fraction.js": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", - "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "node_modules/es-shim-unscopables": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", + "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", + "dev": true, "license": "MIT", - "engines": { - "node": "*" - }, - "funding": { - "type": "patreon", - "url": "https://github.com/sponsors/rawify" + "dependencies": { + "hasown": "^2.0.0" } }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "hasInstallScript": true, + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "dev": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], + "dependencies": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + }, "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/function.prototype.name": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", - "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", - "dev": true, + "node_modules/esbuild": { + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.2.tgz", + "integrity": "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==", + "hasInstallScript": true, "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "functions-have-names": "^1.2.3", - "hasown": "^2.0.2", - "is-callable": "^1.2.7" + "bin": { + "esbuild": "bin/esbuild" }, "engines": { - "node": ">= 0.4" + "node": ">=18" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.24.2", + "@esbuild/android-arm": "0.24.2", + "@esbuild/android-arm64": "0.24.2", + "@esbuild/android-x64": "0.24.2", + "@esbuild/darwin-arm64": "0.24.2", + "@esbuild/darwin-x64": "0.24.2", + "@esbuild/freebsd-arm64": "0.24.2", + "@esbuild/freebsd-x64": "0.24.2", + "@esbuild/linux-arm": "0.24.2", + "@esbuild/linux-arm64": "0.24.2", + "@esbuild/linux-ia32": "0.24.2", + "@esbuild/linux-loong64": "0.24.2", + "@esbuild/linux-mips64el": "0.24.2", + "@esbuild/linux-ppc64": "0.24.2", + "@esbuild/linux-riscv64": "0.24.2", + "@esbuild/linux-s390x": "0.24.2", + "@esbuild/linux-x64": "0.24.2", + "@esbuild/netbsd-arm64": "0.24.2", + "@esbuild/netbsd-x64": "0.24.2", + "@esbuild/openbsd-arm64": "0.24.2", + "@esbuild/openbsd-x64": "0.24.2", + "@esbuild/sunos-x64": "0.24.2", + "@esbuild/win32-arm64": "0.24.2", + "@esbuild/win32-ia32": "0.24.2", + "@esbuild/win32-x64": "0.24.2" } }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "dev": true, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=6" } }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, "license": "MIT", "engines": { - "node": ">=6.9.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/get-intrinsic": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", - "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", + "node_modules/eslint": { + "version": "9.19.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.19.0.tgz", + "integrity": "sha512-ug92j0LepKlbbEv6hD911THhoRHmbdXt2gX+VDABAW/Ir7D3nqKdv5Pf5vtlyY6HQMTEP2skXY43ueqTCWssEA==", "dev": true, "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "function-bind": "^1.1.2", - "get-proto": "^1.0.0", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.19.0", + "@eslint/core": "^0.10.0", + "@eslint/eslintrc": "^3.2.0", + "@eslint/js": "9.19.0", + "@eslint/plugin-kit": "^0.2.5", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.1", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.2.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" }, "engines": { - "node": ">= 0.4" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } } }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "node_modules/eslint-plugin-react": { + "version": "7.37.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.4.tgz", + "integrity": "sha512-BGP0jRmfYyvOyvMoRX/uoUeW+GqNj9y16bPQzqAHf3AYII/tDs+jMN0dBVkl88/OZwNGwrVFxE7riHsXVfy/LQ==", "dev": true, "license": "MIT", "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" + "array-includes": "^3.1.8", + "array.prototype.findlast": "^1.2.5", + "array.prototype.flatmap": "^1.3.3", + "array.prototype.tosorted": "^1.1.4", + "doctrine": "^2.1.0", + "es-iterator-helpers": "^1.2.1", + "estraverse": "^5.3.0", + "hasown": "^2.0.2", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.8", + "object.fromentries": "^2.0.8", + "object.values": "^1.2.1", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.5", + "semver": "^6.3.1", + "string.prototype.matchall": "^4.0.12", + "string.prototype.repeat": "^1.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" } }, - "node_modules/get-stdin": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz", - "integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==", + "node_modules/eslint-plugin-react-hooks": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.1.0.tgz", + "integrity": "sha512-mpJRtPgHN2tNAvZ35AMfqeB3Xqeo273QxrHJsbBEPWODRM4r0yB6jfoROqKEYrOn27UtRPpcpHc2UqyBSuUNTw==", + "dev": true, "license": "MIT", "engines": { "node": ">=10" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" } }, - "node_modules/get-symbol-description": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", - "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "node_modules/eslint-plugin-react-refresh": { + "version": "0.4.18", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.18.tgz", + "integrity": "sha512-IRGEoFn3OKalm3hjfolEWGqoF/jPqeEYFp+C8B0WMzwGwBMvlRDQd06kghDhF0C61uJ6WfSDhEZE/sAQjduKgw==", "dev": true, "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "eslint": ">=8.40" } }, - "node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "license": "ISC", + "node_modules/eslint-scope": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz", + "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==", + "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" }, - "bin": { - "glob": "dist/esm/bin.mjs" + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://opencollective.com/eslint" } }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "license": "Apache-2.0", "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/glob/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/glob/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "license": "ISC", + "node_modules/espree": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", + "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "brace-expansion": "^2.0.1" + "acorn": "^8.14.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.0" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://opencollective.com/eslint" } }, - "node_modules/globals": { - "version": "15.14.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-15.14.0.tgz", - "integrity": "sha512-OkToC372DtlQeje9/zHIo5CT8lRP/FUgEOKBEhU4e0abL7J7CD24fD9ohiLN5hagG/kWCYj4K5oaxxtj2Z0Dig==", + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=0.10" } }, - "node_modules/globalthis": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", - "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, - "license": "MIT", + "license": "BSD-2-Clause", "dependencies": { - "define-properties": "^1.2.1", - "gopd": "^1.0.1" + "estraverse": "^5.2.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=4.0" } }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, - "license": "MIT", + "license": "BSD-2-Clause", "engines": { - "node": ">= 0.4" - }, + "node": ">=4.0" + } + }, + "node_modules/estree-util-is-identifier-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", + "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "license": "ISC" + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" }, - "node_modules/has-bigints": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", - "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true, - "license": "MIT", + "license": "BSD-2-Clause", "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.10.0" } }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, "engines": { - "node": ">=8" + "node": ">=8.6.0" } }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "dev": true, - "license": "MIT", + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", "dependencies": { - "es-define-property": "^1.0.0" + "is-glob": "^4.0.1" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">= 6" } }, - "node_modules/has-proto": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", - "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.0.tgz", + "integrity": "sha512-7SFSRCNjBQIZH/xZR3iy5iQYR8aGBE0h3VG6/cwlbrpdciNYBMotQav8c1XI3HjHH+NikUpP53nPdlZSdWmFzA==", + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fault": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/fault/-/fault-1.0.4.tgz", + "integrity": "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==", "license": "MIT", "dependencies": { - "dunder-proto": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" + "format": "^0.2.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, "license": "MIT", - "engines": { - "node": ">= 0.4" + "dependencies": { + "flat-cache": "^4.0.0" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, + "node_modules/file-selector": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/file-selector/-/file-selector-2.1.2.tgz", + "integrity": "sha512-QgXo+mXTe8ljeqUFaX3QVHc5osSItJ/Km+xpocx0aSqWGMSCf6qYs/VnzZgS864Pjn5iceMRFigeAV7AfTlaig==", "license": "MIT", "dependencies": { - "has-symbols": "^1.0.3" + "tslib": "^2.7.0" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 12" } }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "license": "MIT", "dependencies": { - "function-bind": "^1.1.2" + "to-regex-range": "^5.0.1" }, "engines": { - "node": ">= 0.4" + "node": ">=8" } }, - "node_modules/hast-util-parse-selector": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz", - "integrity": "sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==", + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/hastscript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-6.0.0.tgz", - "integrity": "sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==", + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, "license": "MIT", "dependencies": { - "@types/hast": "^2.0.0", - "comma-separated-tokens": "^1.0.0", - "hast-util-parse-selector": "^2.0.0", - "property-information": "^5.0.0", - "space-separated-tokens": "^1.0.0" + "flatted": "^3.2.9", + "keyv": "^4.5.4" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/highlight.js": { - "version": "10.7.3", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", - "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", - "license": "BSD-3-Clause", "engines": { - "node": "*" + "node": ">=16" } }, - "node_modules/highlightjs-vue": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/highlightjs-vue/-/highlightjs-vue-1.0.0.tgz", - "integrity": "sha512-PDEfEF102G23vHmPhLyPboFCD+BkMGu+GuJe2d9/eH4FsCwvgBpnc9n0pGE+ffKdph38s6foEZiEjdgHdzp+IA==", - "license": "CC0-1.0" - }, - "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "node_modules/flatted": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", + "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } + "license": "ISC" }, - "node_modules/immer": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/immer/-/immer-10.1.1.tgz", - "integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==", + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/immer" + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } } }, - "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "node_modules/for-each": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.4.tgz", + "integrity": "sha512-kKaIINnFpzW6ffJNDjjyjrk21BkDx38c0xa/klsT8VzLCaMEefv4ZTacrcVR4DmgTeBra++jMDAfS/tS799YDw==", "dev": true, "license": "MIT", "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" + "is-callable": "^1.2.7" }, "engines": { - "node": ">=6" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "license": "MIT", + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, "engines": { - "node": ">=0.8.19" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/internal-slot": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", - "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", - "dev": true, + "node_modules/form-data": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", "license": "MIT", "dependencies": { - "es-errors": "^1.3.0", - "hasown": "^2.0.2", - "side-channel": "^1.1.0" + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" }, "engines": { - "node": ">= 0.4" + "node": ">= 6" } }, - "node_modules/is-alphabetical": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", - "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "node_modules/format": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", + "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==", + "engines": { + "node": ">=0.4.x" } }, - "node_modules/is-alphanumerical": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz", - "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==", + "node_modules/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", "license": "MIT", - "dependencies": { - "is-alphabetical": "^1.0.0", - "is-decimal": "^1.0.0" + "engines": { + "node": "*" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "type": "patreon", + "url": "https://github.com/sponsors/rawify" } }, - "node_modules/is-array-buffer": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", - "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", - "dev": true, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "get-intrinsic": "^1.2.6" - }, + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">= 0.4" - }, + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true, - "license": "MIT" - }, - "node_modules/is-async-function": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", - "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "node_modules/function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", "dev": true, "license": "MIT", "dependencies": { - "async-function": "^1.0.0", + "call-bind": "^1.0.8", "call-bound": "^1.0.3", - "get-proto": "^1.0.1", - "has-tostringtag": "^1.0.2", - "safe-regex-test": "^1.1.0" + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" }, "engines": { "node": ">= 0.4" @@ -6043,14 +6542,43 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-bigint": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", - "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", + "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", "dev": true, "license": "MIT", "dependencies": { - "has-bigints": "^1.0.2" + "call-bind-apply-helpers": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "function-bind": "^1.1.2", + "get-proto": "^1.0.0", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -6059,27 +6587,42 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, "license": "MIT", "dependencies": { - "binary-extensions": "^2.0.0" + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" }, "engines": { - "node": ">=8" + "node": ">= 0.4" } }, - "node_modules/is-boolean-object": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", - "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "node_modules/get-stdin": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz", + "integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", "dev": true, "license": "MIT", "dependencies": { "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" }, "engines": { "node": ">= 0.4" @@ -6088,864 +6631,2662 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/globals": { + "version": "15.14.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.14.0.tgz", + "integrity": "sha512-OkToC372DtlQeje9/zHIo5CT8lRP/FUgEOKBEhU4e0abL7J7CD24fD9ohiLN5hagG/kWCYj4K5oaxxtj2Z0Dig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/hachure-fill": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/hachure-fill/-/hachure-fill-0.5.2.tgz", + "integrity": "sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==" + }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "dev": true, "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hast-util-from-parse5": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.3.tgz", + "integrity": "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "devlop": "^1.0.0", + "hastscript": "^9.0.0", + "property-information": "^7.0.0", + "vfile": "^6.0.0", + "vfile-location": "^5.0.0", + "web-namespaces": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-parse5/node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/hast-util-from-parse5/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" + }, + "node_modules/hast-util-from-parse5/node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/hast-util-from-parse5/node_modules/hast-util-parse-selector": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", + "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-parse5/node_modules/hastscript": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.1.tgz", + "integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-parse-selector": "^4.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-parse5/node_modules/property-information": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", + "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/hast-util-from-parse5/node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/hast-util-parse-selector": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz", + "integrity": "sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-raw": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.1.0.tgz", + "integrity": "sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "@ungap/structured-clone": "^1.0.0", + "hast-util-from-parse5": "^8.0.0", + "hast-util-to-parse5": "^8.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "parse5": "^7.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-raw/node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/hast-util-raw/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" + }, + "node_modules/hast-util-to-jsx-runtime": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", + "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-js": "^1.0.0", + "unist-util-position": "^5.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-jsx-runtime/node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/hast-util-to-jsx-runtime/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" + }, + "node_modules/hast-util-to-jsx-runtime/node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/hast-util-to-jsx-runtime/node_modules/property-information": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", + "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/hast-util-to-jsx-runtime/node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/hast-util-to-parse5": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-8.0.1.tgz", + "integrity": "sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA==", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-parse5/node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/hast-util-to-parse5/node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/hast-util-to-parse5/node_modules/property-information": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", + "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/hast-util-to-parse5/node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace/node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/hastscript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-6.0.0.tgz", + "integrity": "sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==", + "license": "MIT", + "dependencies": { + "@types/hast": "^2.0.0", + "comma-separated-tokens": "^1.0.0", + "hast-util-parse-selector": "^2.0.0", + "property-information": "^5.0.0", + "space-separated-tokens": "^1.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } + }, + "node_modules/highlightjs-vue": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/highlightjs-vue/-/highlightjs-vue-1.0.0.tgz", + "integrity": "sha512-PDEfEF102G23vHmPhLyPboFCD+BkMGu+GuJe2d9/eH4FsCwvgBpnc9n0pGE+ffKdph38s6foEZiEjdgHdzp+IA==", + "license": "CC0-1.0" + }, + "node_modules/html-url-attributes": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz", + "integrity": "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/html-void-elements": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", + "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/immer": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/immer/-/immer-10.1.1.tgz", + "integrity": "sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inline-style-parser": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz", + "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==" + }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "engines": { + "node": ">=12" + } + }, + "node_modules/is-alphabetical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", + "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz", + "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==", + "license": "MIT", + "dependencies": { + "is-alphabetical": "^1.0.0", + "is-decimal": "^1.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-decimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz", + "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-function": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", + "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-proto": "^1.0.0", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-hexadecimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz", + "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/iterator.prototype": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", + "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "get-proto": "^1.0.0", + "has-symbols": "^1.1.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jiti": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", + "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsx-ast-utils": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", + "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "object.assign": "^4.1.4", + "object.values": "^1.1.6" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/katex": { + "version": "0.16.38", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.38.tgz", + "integrity": "sha512-cjHooZUmIAUmDsHBN+1n8LaZdpmbj03LtYeYPyuYB7OuloiaeaV6N4LcfjcnHVzGWjVQmKrxxTrpDcmSzEZQwQ==", + "funding": [ + "https://opencollective.com/katex", + "https://github.com/sponsors/katex" + ], + "dependencies": { + "commander": "^8.3.0" + }, + "bin": { + "katex": "cli.js" + } + }, + "node_modules/katex/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "engines": { + "node": ">= 12" + } + }, + "node_modules/keyborg": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/keyborg/-/keyborg-2.6.0.tgz", + "integrity": "sha512-o5kvLbuTF+o326CMVYpjlaykxqYP9DphFQZ2ZpgrvBouyvOxyEB7oqe8nOLFpiV5VCtz0D3pt8gXQYWpLpBnmA==", + "license": "MIT" + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/khroma": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/khroma/-/khroma-2.1.0.tgz", + "integrity": "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==" + }, + "node_modules/langium": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/langium/-/langium-4.2.1.tgz", + "integrity": "sha512-zu9QWmjpzJcomzdJQAHgDVhLGq5bLosVak1KVa40NzQHXfqr4eAHupvnPOVXEoLkg6Ocefvf/93d//SB7du4YQ==", + "dependencies": { + "chevrotain": "~11.1.1", + "chevrotain-allstar": "~0.3.1", + "vscode-languageserver": "~9.0.1", + "vscode-languageserver-textdocument": "~1.0.11", + "vscode-uri": "~3.1.0" + }, + "engines": { + "node": ">=20.10.0", + "npm": ">=10.2.3" + } + }, + "node_modules/layout-base": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-1.0.2.tgz", + "integrity": "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==" + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lightningcss": { + "version": "1.29.1", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.29.1.tgz", + "integrity": "sha512-FmGoeD4S05ewj+AkhTY+D+myDvXI6eL27FjHIjoyUkO/uw7WZD1fBVs0QxeYWa7E17CUHJaYX/RUGISCtcrG4Q==", + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^1.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-darwin-arm64": "1.29.1", + "lightningcss-darwin-x64": "1.29.1", + "lightningcss-freebsd-x64": "1.29.1", + "lightningcss-linux-arm-gnueabihf": "1.29.1", + "lightningcss-linux-arm64-gnu": "1.29.1", + "lightningcss-linux-arm64-musl": "1.29.1", + "lightningcss-linux-x64-gnu": "1.29.1", + "lightningcss-linux-x64-musl": "1.29.1", + "lightningcss-win32-arm64-msvc": "1.29.1", + "lightningcss-win32-x64-msvc": "1.29.1" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.29.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.29.1.tgz", + "integrity": "sha512-HtR5XJ5A0lvCqYAoSv2QdZZyoHNttBpa5EP9aNuzBQeKGfbyH5+UipLWvVzpP4Uml5ej4BYs5I9Lco9u1fECqw==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.29.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.29.1.tgz", + "integrity": "sha512-k33G9IzKUpHy/J/3+9MCO4e+PzaFblsgBjSGlpAaFikeBFm8B/CkO3cKU9oI4g+fjS2KlkLM/Bza9K/aw8wsNA==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.29.1", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.29.1.tgz", + "integrity": "sha512-0SUW22fv/8kln2LnIdOCmSuXnxgxVC276W5KLTwoehiO0hxkacBxjHOL5EtHD8BAXg2BvuhsJPmVMasvby3LiQ==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.29.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.29.1.tgz", + "integrity": "sha512-sD32pFvlR0kDlqsOZmYqH/68SqUMPNj+0pucGxToXZi4XZgZmqeX/NkxNKCPsswAXU3UeYgDSpGhu05eAufjDg==", + "cpu": [ + "arm" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.29.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.29.1.tgz", + "integrity": "sha512-0+vClRIZ6mmJl/dxGuRsE197o1HDEeeRk6nzycSy2GofC2JsY4ifCRnvUWf/CUBQmlrvMzt6SMQNMSEu22csWQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.29.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.29.1.tgz", + "integrity": "sha512-UKMFrG4rL/uHNgelBsDwJcBqVpzNJbzsKkbI3Ja5fg00sgQnHw/VrzUTEc4jhZ+AN2BvQYz/tkHu4vt1kLuJyw==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.29.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.29.1.tgz", + "integrity": "sha512-u1S+xdODy/eEtjADqirA774y3jLcm8RPtYztwReEXoZKdzgsHYPl0s5V52Tst+GKzqjebkULT86XMSxejzfISw==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.29.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.29.1.tgz", + "integrity": "sha512-L0Tx0DtaNUTzXv0lbGCLB/c/qEADanHbu4QdcNOXLIe1i8i22rZRpbT3gpWYsCh9aSL9zFujY/WmEXIatWvXbw==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.29.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.29.1.tgz", + "integrity": "sha512-QoOVnkIEFfbW4xPi+dpdft/zAKmgLgsRHfJalEPYuJDOWf7cLQzYg0DEh8/sn737FaeMJxHZRc1oBreiwZCjog==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.29.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.29.1.tgz", + "integrity": "sha512-NygcbThNBe4JElP+olyTI/doBNGJvLs3bFCRPdvuCcxZCcCZ71B858IHpdm7L1btZex0FvCmM17FK98Y9MRy1Q==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">= 0.4" + "node": ">= 12.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, "engines": { - "node": ">= 0.4" + "node": ">=14" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/antonk52" } }, - "node_modules/is-data-view": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", - "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "license": "MIT", "dependencies": { - "call-bound": "^1.0.2", - "get-intrinsic": "^1.2.6", - "is-typed-array": "^1.1.13" + "p-locate": "^5.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-date-object": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", - "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "node_modules/lodash-es": { + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.23.tgz", + "integrity": "sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "license": "MIT" }, - "node_modules/is-decimal": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz", - "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==", - "license": "MIT", + "node_modules/longest-streak": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", "license": "MIT", - "engines": { - "node": ">=0.10.0" + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" } }, - "node_modules/is-finalizationregistry": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", - "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", - "dev": true, - "license": "MIT", + "node_modules/lottie-react": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/lottie-react/-/lottie-react-2.4.1.tgz", + "integrity": "sha512-LQrH7jlkigIIv++wIyrOYFLHSKQpEY4zehPicL9bQsrt1rnoKRYCYgpCUe5maqylNtacy58/sQDZTkwMcTRxZw==", "dependencies": { - "call-bound": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" + "lottie-web": "^5.10.2" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "license": "MIT", - "engines": { - "node": ">=8" - } + "node_modules/lottie-web": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/lottie-web/-/lottie-web-5.13.0.tgz", + "integrity": "sha512-+gfBXl6sxXMPe8tKQm7qzLnUy5DUPJPKIyRHwtpCpyUEYjHYRJC/5gjUvdkuO2c3JllrPtHXH5UJJK8LRYl5yQ==" }, - "node_modules/is-generator-function": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", - "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", "dev": true, "license": "MIT", "dependencies": { - "call-bound": "^1.0.3", - "get-proto": "^1.0.0", - "has-tostringtag": "^1.0.2", - "safe-regex-test": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "tslib": "^2.0.3" } }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "node_modules/lowlight": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-1.20.0.tgz", + "integrity": "sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==", "license": "MIT", "dependencies": { - "is-extglob": "^2.1.1" + "fault": "^1.0.0", + "highlight.js": "~10.7.0" }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-hexadecimal": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz", - "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==", - "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/is-map": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", - "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" } }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "license": "MIT", - "engines": { - "node": ">=0.12.0" + "node_modules/lucide-react": { + "version": "0.471.2", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.471.2.tgz", + "integrity": "sha512-A8fDycQxGeaSOTaI7Bm4fg8LBXO7Qr9ORAX47bDRvugCsjLIliugQO0PkKFoeAD57LIQwlWKd3NIQ3J7hYp84g==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, - "node_modules/is-number-object": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", - "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "node_modules/magic-string": { + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", "dev": true, "license": "MIT", "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/markdown-table": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", + "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/is-regex": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", - "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "gopd": "^1.2.0", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" + "node_modules/marked": { + "version": "16.4.2", + "resolved": "https://registry.npmjs.org/marked/-/marked-16.4.2.tgz", + "integrity": "sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==", + "bin": { + "marked": "bin/marked.js" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">= 20" } }, - "node_modules/is-set": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", - "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", - "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", - "dev": true, - "license": "MIT", + "node_modules/mdast-util-find-and-replace": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", + "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==", "dependencies": { - "call-bound": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" + "@types/mdast": "^4.0.0", + "escape-string-regexp": "^5.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/is-string": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", - "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" + "node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "engines": { + "node": ">=12" }, - "engines": { - "node": ">= 0.4" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mdast-util-from-markdown": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.3.tgz", + "integrity": "sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/is-symbol": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", - "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", - "dev": true, - "license": "MIT", + "node_modules/mdast-util-from-markdown/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" + }, + "node_modules/mdast-util-gfm": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", + "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==", "dependencies": { - "call-bound": "^1.0.2", - "has-symbols": "^1.1.0", - "safe-regex-test": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-gfm-autolink-literal": "^2.0.0", + "mdast-util-gfm-footnote": "^2.0.0", + "mdast-util-gfm-strikethrough": "^2.0.0", + "mdast-util-gfm-table": "^2.0.0", + "mdast-util-gfm-task-list-item": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/is-typed-array": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", - "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", - "dev": true, - "license": "MIT", + "node_modules/mdast-util-gfm-autolink-literal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", + "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", "dependencies": { - "which-typed-array": "^1.1.16" - }, - "engines": { - "node": ">= 0.4" + "@types/mdast": "^4.0.0", + "ccount": "^2.0.0", + "devlop": "^1.0.0", + "mdast-util-find-and-replace": "^3.0.0", + "micromark-util-character": "^2.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/is-weakmap": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", - "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" + "node_modules/mdast-util-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/is-weakref": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", - "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", - "dev": true, - "license": "MIT", + "node_modules/mdast-util-gfm-strikethrough": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", + "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", "dependencies": { - "call-bound": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/is-weakset": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", - "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", - "dev": true, - "license": "MIT", + "node_modules/mdast-util-gfm-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", + "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", "dependencies": { - "call-bound": "^1.0.3", - "get-intrinsic": "^1.2.6" - }, - "engines": { - "node": ">= 0.4" + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "markdown-table": "^3.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true, - "license": "MIT" - }, - "node_modules/isexe": { + "node_modules/mdast-util-gfm-task-list-item": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "license": "ISC" - }, - "node_modules/iterator.prototype": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", - "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", - "dev": true, - "license": "MIT", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", + "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", "dependencies": { - "define-data-property": "^1.1.4", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.6", - "get-proto": "^1.0.0", - "has-symbols": "^1.1.0", - "set-function-name": "^2.0.2" + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" }, - "engines": { - "node": ">= 0.4" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "license": "BlueOak-1.0.0", + "node_modules/mdast-util-mdx-expression": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", + "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", "dependencies": { - "@isaacs/cliui": "^8.0.2" + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" }, "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/jiti": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", - "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", - "license": "MIT", - "bin": { - "jiti": "lib/jiti-cli.mjs" + "node_modules/mdast-util-mdx-expression/node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dependencies": { + "@types/unist": "*" } }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "license": "MIT" - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" + "node_modules/mdast-util-mdx-jsx": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz", + "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" + "node_modules/mdast-util-mdx-jsx/node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dependencies": { + "@types/unist": "*" } }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true, - "license": "MIT" + "node_modules/mdast-util-mdx-jsx/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true, - "license": "MIT" + "node_modules/mdast-util-mdx-jsx/node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" + "node_modules/mdast-util-mdx-jsx/node_modules/character-reference-invalid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", + "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true, - "license": "MIT" + "node_modules/mdast-util-mdx-jsx/node_modules/is-alphabetical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", + "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "license": "MIT", - "bin": { - "json5": "lib/cli.js" + "node_modules/mdast-util-mdx-jsx/node_modules/is-alphanumerical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", + "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-decimal": "^2.0.0" }, - "engines": { - "node": ">=6" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/jsx-ast-utils": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", - "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", - "dev": true, - "license": "MIT", + "node_modules/mdast-util-mdx-jsx/node_modules/is-decimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", + "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/mdast-util-mdx-jsx/node_modules/is-hexadecimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", + "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/mdast-util-mdx-jsx/node_modules/parse-entities": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", + "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", "dependencies": { - "array-includes": "^3.1.6", - "array.prototype.flat": "^1.3.1", - "object.assign": "^4.1.4", - "object.values": "^1.1.6" + "@types/unist": "^2.0.0", + "character-entities-legacy": "^3.0.0", + "character-reference-invalid": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0", + "is-hexadecimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/mdast-util-mdx-jsx/node_modules/parse-entities/node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==" + }, + "node_modules/mdast-util-mdxjs-esm": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", + "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" }, - "engines": { - "node": ">=4.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/keyborg": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/keyborg/-/keyborg-2.6.0.tgz", - "integrity": "sha512-o5kvLbuTF+o326CMVYpjlaykxqYP9DphFQZ2ZpgrvBouyvOxyEB7oqe8nOLFpiV5VCtz0D3pt8gXQYWpLpBnmA==", - "license": "MIT" - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "license": "MIT", + "node_modules/mdast-util-mdxjs-esm/node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", "dependencies": { - "json-buffer": "3.0.1" + "@types/unist": "*" } }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "license": "MIT", + "node_modules/mdast-util-phrasing": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", + "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" + "@types/mdast": "^4.0.0", + "unist-util-is": "^6.0.0" }, - "engines": { - "node": ">= 0.8.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/lightningcss": { - "version": "1.29.1", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.29.1.tgz", - "integrity": "sha512-FmGoeD4S05ewj+AkhTY+D+myDvXI6eL27FjHIjoyUkO/uw7WZD1fBVs0QxeYWa7E17CUHJaYX/RUGISCtcrG4Q==", - "license": "MPL-2.0", + "node_modules/mdast-util-to-hast": { + "version": "13.2.1", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", + "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", "dependencies": { - "detect-libc": "^1.0.3" - }, - "engines": { - "node": ">= 12.0.0" + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/parcel" + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast/node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/mdast-util-to-markdown": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", + "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" }, - "optionalDependencies": { - "lightningcss-darwin-arm64": "1.29.1", - "lightningcss-darwin-x64": "1.29.1", - "lightningcss-freebsd-x64": "1.29.1", - "lightningcss-linux-arm-gnueabihf": "1.29.1", - "lightningcss-linux-arm64-gnu": "1.29.1", - "lightningcss-linux-arm64-musl": "1.29.1", - "lightningcss-linux-x64-gnu": "1.29.1", - "lightningcss-linux-x64-musl": "1.29.1", - "lightningcss-win32-arm64-msvc": "1.29.1", - "lightningcss-win32-x64-msvc": "1.29.1" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" } }, - "node_modules/lightningcss-darwin-arm64": { - "version": "1.29.1", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.29.1.tgz", - "integrity": "sha512-HtR5XJ5A0lvCqYAoSv2QdZZyoHNttBpa5EP9aNuzBQeKGfbyH5+UipLWvVzpP4Uml5ej4BYs5I9Lco9u1fECqw==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" + "node_modules/mdast-util-to-markdown/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" + }, + "node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "dependencies": { + "@types/mdast": "^4.0.0" }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/parcel" + "url": "https://opencollective.com/unified" } }, - "node_modules/lightningcss-darwin-x64": { - "version": "1.29.1", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.29.1.tgz", - "integrity": "sha512-k33G9IzKUpHy/J/3+9MCO4e+PzaFblsgBjSGlpAaFikeBFm8B/CkO3cKU9oI4g+fjS2KlkLM/Bza9K/aw8wsNA==", - "cpu": [ - "x64" + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/mermaid": { + "version": "11.13.0", + "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.13.0.tgz", + "integrity": "sha512-fEnci+Immw6lKMFI8sqzjlATTyjLkRa6axrEgLV2yHTfv8r+h1wjFbV6xeRtd4rUV1cS4EpR9rwp3Rci7TRWDw==", + "dependencies": { + "@braintree/sanitize-url": "^7.1.1", + "@iconify/utils": "^3.0.2", + "@mermaid-js/parser": "^1.0.1", + "@types/d3": "^7.4.3", + "@upsetjs/venn.js": "^2.0.0", + "cytoscape": "^3.33.1", + "cytoscape-cose-bilkent": "^4.1.0", + "cytoscape-fcose": "^2.2.0", + "d3": "^7.9.0", + "d3-sankey": "^0.12.3", + "dagre-d3-es": "7.0.14", + "dayjs": "^1.11.19", + "dompurify": "^3.3.1", + "katex": "^0.16.25", + "khroma": "^2.1.0", + "lodash-es": "^4.17.23", + "marked": "^16.3.0", + "roughjs": "^4.6.6", + "stylis": "^4.3.6", + "ts-dedent": "^2.2.0", + "uuid": "^11.1.0" + } + }, + "node_modules/messagebar": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/messagebar/-/messagebar-0.2.1.tgz", + "integrity": "sha512-LiEY96DSg4fA+IF41b+ZnBox1M1yjwr3OboELC4rR+da2sb8y6eyxhSsdmKHk+vL0oWZTyPGS9NJRTBX7LbbHA==" + }, + "node_modules/micromark": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", + "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", + "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } ], - "engines": { - "node": ">= 12.0.0" + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", + "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", + "dependencies": { + "micromark-extension-gfm-autolink-literal": "^2.0.0", + "micromark-extension-gfm-footnote": "^2.0.0", + "micromark-extension-gfm-strikethrough": "^2.0.0", + "micromark-extension-gfm-table": "^2.0.0", + "micromark-extension-gfm-tagfilter": "^2.0.0", + "micromark-extension-gfm-task-list-item": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-types": "^2.0.0" }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/parcel" + "url": "https://opencollective.com/unified" } }, - "node_modules/lightningcss-freebsd-x64": { - "version": "1.29.1", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.29.1.tgz", - "integrity": "sha512-0SUW22fv/8kln2LnIdOCmSuXnxgxVC276W5KLTwoehiO0hxkacBxjHOL5EtHD8BAXg2BvuhsJPmVMasvby3LiQ==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 12.0.0" + "node_modules/micromark-extension-gfm-autolink-literal": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", + "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/parcel" + "url": "https://opencollective.com/unified" } }, - "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.29.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.29.1.tgz", - "integrity": "sha512-sD32pFvlR0kDlqsOZmYqH/68SqUMPNj+0pucGxToXZi4XZgZmqeX/NkxNKCPsswAXU3UeYgDSpGhu05eAufjDg==", - "cpu": [ - "arm" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" + "node_modules/micromark-extension-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", + "dependencies": { + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/parcel" + "url": "https://opencollective.com/unified" } }, - "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.29.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.29.1.tgz", - "integrity": "sha512-0+vClRIZ6mmJl/dxGuRsE197o1HDEeeRk6nzycSy2GofC2JsY4ifCRnvUWf/CUBQmlrvMzt6SMQNMSEu22csWQ==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" + "node_modules/micromark-extension-gfm-strikethrough": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", + "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/parcel" + "url": "https://opencollective.com/unified" } }, - "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.29.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.29.1.tgz", - "integrity": "sha512-UKMFrG4rL/uHNgelBsDwJcBqVpzNJbzsKkbI3Ja5fg00sgQnHw/VrzUTEc4jhZ+AN2BvQYz/tkHu4vt1kLuJyw==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" + "node_modules/micromark-extension-gfm-table": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", + "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/parcel" + "url": "https://opencollective.com/unified" } }, - "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.29.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.29.1.tgz", - "integrity": "sha512-u1S+xdODy/eEtjADqirA774y3jLcm8RPtYztwReEXoZKdzgsHYPl0s5V52Tst+GKzqjebkULT86XMSxejzfISw==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" + "node_modules/micromark-extension-gfm-tagfilter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", + "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", + "dependencies": { + "micromark-util-types": "^2.0.0" }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/parcel" + "url": "https://opencollective.com/unified" } }, - "node_modules/lightningcss-linux-x64-musl": { - "version": "1.29.1", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.29.1.tgz", - "integrity": "sha512-L0Tx0DtaNUTzXv0lbGCLB/c/qEADanHbu4QdcNOXLIe1i8i22rZRpbT3gpWYsCh9aSL9zFujY/WmEXIatWvXbw==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" + "node_modules/micromark-extension-gfm-task-list-item": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", + "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/parcel" + "url": "https://opencollective.com/unified" } }, - "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.29.1", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.29.1.tgz", - "integrity": "sha512-QoOVnkIEFfbW4xPi+dpdft/zAKmgLgsRHfJalEPYuJDOWf7cLQzYg0DEh8/sn737FaeMJxHZRc1oBreiwZCjog==", - "cpu": [ - "arm64" + "node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.29.1", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.29.1.tgz", - "integrity": "sha512-NygcbThNBe4JElP+olyTI/doBNGJvLs3bFCRPdvuCcxZCcCZ71B858IHpdm7L1btZex0FvCmM17FK98Y9MRy1Q==", - "cpu": [ - "x64" + "node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/lilconfig": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", - "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antonk52" + "node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "license": "MIT" + "node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "license": "MIT", + "node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "micromark-util-symbol": "^2.0.0" } }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true, - "license": "MIT" + "node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "license": "MIT", + "node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/lower-case": { + "node_modules/micromark-util-decode-numeric-character-reference": { "version": "2.0.2", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", - "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", - "dev": true, - "license": "MIT", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "dependencies": { - "tslib": "^2.0.3" + "micromark-util-symbol": "^2.0.0" } }, - "node_modules/lowlight": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-1.20.0.tgz", - "integrity": "sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==", - "license": "MIT", + "node_modules/micromark-util-decode-string": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "dependencies": { - "fault": "^1.0.0", - "highlight.js": "~10.7.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" } }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "license": "ISC", + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "dependencies": { - "yallist": "^3.0.2" + "micromark-util-symbol": "^2.0.0" } }, - "node_modules/lucide-react": { - "version": "0.471.2", - "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.471.2.tgz", - "integrity": "sha512-A8fDycQxGeaSOTaI7Bm4fg8LBXO7Qr9ORAX47bDRvugCsjLIliugQO0PkKFoeAD57LIQwlWKd3NIQ3J7hYp84g==", - "license": "ISC", - "peerDependencies": { - "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + "node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "micromark-util-types": "^2.0.0" } }, - "node_modules/magic-string": { - "version": "0.30.17", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", - "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", - "dev": true, - "license": "MIT", + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0" + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" } }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" + "node_modules/micromark-util-subtokenize": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", + "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" } }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "license": "MIT", - "engines": { - "node": ">= 8" - } + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] + }, + "node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ] }, "node_modules/micromatch": { "version": "4.0.8", @@ -7003,6 +9344,17 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/mlly": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.1.tgz", + "integrity": "sha512-SnL6sNutTwRWWR/vcmCYHSADjiEesp5TGQQ0pXyLhW5IoeibRlF/CbSLailbB3CNqJUk9cVJ9dUDnbD7GrcHBQ==", + "dependencies": { + "acorn": "^8.16.0", + "pathe": "^2.0.3", + "pkg-types": "^1.3.1", + "ufo": "^1.6.3" + } + }, "node_modules/moo": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/moo/-/moo-0.5.2.tgz", @@ -7013,7 +9365,6 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, "license": "MIT" }, "node_modules/mz": { @@ -7304,6 +9655,11 @@ "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", "license": "BlueOak-1.0.0" }, + "node_modules/package-manager-detector": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.6.0.tgz", + "integrity": "sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -7354,6 +9710,33 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/path-data-parser": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/path-data-parser/-/path-data-parser-0.1.0.tgz", + "integrity": "sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==" + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -7411,6 +9794,11 @@ "node": ">=8" } }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==" + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -7447,6 +9835,30 @@ "node": ">= 6" } }, + "node_modules/pkg-types": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" + } + }, + "node_modules/points-on-curve": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/points-on-curve/-/points-on-curve-0.2.0.tgz", + "integrity": "sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==" + }, + "node_modules/points-on-path": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/points-on-path/-/points-on-path-0.2.1.tgz", + "integrity": "sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==", + "dependencies": { + "path-data-parser": "0.1.0", + "points-on-curve": "0.2.0" + } + }, "node_modules/possible-typed-array-names": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", @@ -7790,6 +10202,40 @@ "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "license": "MIT" }, + "node_modules/react-markdown": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-10.1.0.tgz", + "integrity": "sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "hast-util-to-jsx-runtime": "^2.0.0", + "html-url-attributes": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.0.0", + "unified": "^11.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "@types/react": ">=18", + "react": ">=18" + } + }, + "node_modules/react-markdown/node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dependencies": { + "@types/unist": "*" + } + }, "node_modules/react-redux": { "version": "9.2.0", "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz", @@ -7990,6 +10436,98 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/rehype-raw": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/rehype-raw/-/rehype-raw-7.0.0.tgz", + "integrity": "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-raw": "^9.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-raw/node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/remark-gfm": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz", + "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-gfm": "^3.0.0", + "micromark-extension-gfm": "^3.0.0", + "remark-parse": "^11.0.0", + "remark-stringify": "^11.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-parse": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", + "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-rehype": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz", + "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "mdast-util-to-hast": "^13.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-rehype/node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/remark-stringify": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", + "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-to-markdown": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/reselect": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz", @@ -8043,6 +10581,11 @@ "node": ">=0.10.0" } }, + "node_modules/robust-predicates": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz", + "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==" + }, "node_modules/rollup": { "version": "4.34.4", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.34.4.tgz", @@ -8104,6 +10647,17 @@ "typescript": "^4.5 || ^5.0" } }, + "node_modules/roughjs": { + "version": "4.6.6", + "resolved": "https://registry.npmjs.org/roughjs/-/roughjs-4.6.6.tgz", + "integrity": "sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==", + "dependencies": { + "hachure-fill": "^0.5.2", + "path-data-parser": "^0.1.0", + "points-on-curve": "^0.2.0", + "points-on-path": "^0.2.1" + } + }, "node_modules/rtl-css-js": { "version": "1.16.1", "resolved": "https://registry.npmjs.org/rtl-css-js/-/rtl-css-js-1.16.1.tgz", @@ -8136,6 +10690,11 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==" + }, "node_modules/safe-array-concat": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", @@ -8191,6 +10750,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, "node_modules/scheduler": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", @@ -8576,6 +11140,28 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/stringify-entities/node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/strip-ansi": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", @@ -8626,11 +11212,26 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/style-to-js": { + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.21.tgz", + "integrity": "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==", + "dependencies": { + "style-to-object": "1.0.14" + } + }, + "node_modules/style-to-object": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.14.tgz", + "integrity": "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==", + "dependencies": { + "inline-style-parser": "0.2.7" + } + }, "node_modules/stylis": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.5.tgz", - "integrity": "sha512-K7npNOKGRYuhAFFzkzMGfxFDpN6gDwf8hcMiE+uveTVbBgm93HrNP3ZDUpKqzZ4pG7TP6fmb+EMAQPjq9FqqvA==", - "license": "MIT" + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz", + "integrity": "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==" }, "node_modules/sucrase": { "version": "3.35.0", @@ -8802,6 +11403,14 @@ "node": ">=0.8" } }, + "node_modules/tinyexec": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", + "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", + "engines": { + "node": ">=18" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -8814,6 +11423,32 @@ "node": ">=8.0" } }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/trough": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", + "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/ts-dedent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz", + "integrity": "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==", + "engines": { + "node": ">=6.10" + } + }, "node_modules/ts-interface-checker": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", @@ -8938,6 +11573,11 @@ "node": ">=14.17" } }, + "node_modules/ufo": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.3.tgz", + "integrity": "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==" + }, "node_modules/unbox-primitive": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", @@ -8957,6 +11597,117 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/unified": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", + "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", + "dependencies": { + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unified/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" + }, + "node_modules/unist-util-is": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", + "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-is/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" + }, + "node_modules/unist-util-visit": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz", + "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", + "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" + }, + "node_modules/unist-util-visit/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" + }, "node_modules/update-browserslist-db": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz", @@ -9025,18 +11776,71 @@ "license": "MIT" }, "node_modules/uuid": { - "version": "11.0.5", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.0.5.tgz", - "integrity": "sha512-508e6IcKLrhxKdBbcA2b4KQZlLVp2+J5UwQ6F7Drckkc5N9ZJwFa4TgWtsww9UG8fGHbm6gbV19TdM5pQ4GaIA==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" ], - "license": "MIT", "bin": { "uuid": "dist/esm/bin/uuid" } }, + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-location": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.3.tgz", + "integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-location/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" + }, + "node_modules/vfile-message": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", + "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" + }, + "node_modules/vfile/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==" + }, "node_modules/vite": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/vite/-/vite-6.1.0.tgz", @@ -9123,6 +11927,58 @@ "vite": ">=2.6.0" } }, + "node_modules/vscode-jsonrpc": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", + "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/vscode-languageserver": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-9.0.1.tgz", + "integrity": "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==", + "dependencies": { + "vscode-languageserver-protocol": "3.17.5" + }, + "bin": { + "installServerIntoExtension": "bin/installServerIntoExtension" + } + }, + "node_modules/vscode-languageserver-protocol": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz", + "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==", + "dependencies": { + "vscode-jsonrpc": "8.2.0", + "vscode-languageserver-types": "3.17.5" + } + }, + "node_modules/vscode-languageserver-textdocument": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz", + "integrity": "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==" + }, + "node_modules/vscode-languageserver-types": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", + "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==" + }, + "node_modules/vscode-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", + "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==" + }, + "node_modules/web-namespaces": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", + "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -9364,6 +12220,15 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } } } } diff --git a/src/frontend/package.json b/src/frontend/package.json index 04fe805..8b4ff23 100644 --- a/src/frontend/package.json +++ b/src/frontend/package.json @@ -27,6 +27,7 @@ "js-yaml": "^4.1.0", "lottie-react": "^2.4.1", "lucide-react": "^0.471.2", + "mermaid": "^11.13.0", "messagebar": "^0.2.1", "postcss": "^8.5.0", "react": "^18.3.1", @@ -37,6 +38,8 @@ "react-redux": "^9.1.2", "react-router-dom": "^7.1.3", "react-syntax-highlighter": "^15.6.1", + "rehype-raw": "^7.0.0", + "remark-gfm": "^4.0.1", "sql-formatter": "^15.4.11", "tailwind-merge": "^2.6.0", "tailwindcss": "^3.4.17", diff --git a/src/frontend/src/commonComponents/ProgressModal/progressModal.tsx b/src/frontend/src/commonComponents/ProgressModal/progressModal.tsx index 3abaa6b..61e9d1b 100644 --- a/src/frontend/src/commonComponents/ProgressModal/progressModal.tsx +++ b/src/frontend/src/commonComponents/ProgressModal/progressModal.tsx @@ -1,12 +1,12 @@ import React from "react"; -import { - Dialog, - DialogSurface, - DialogBody, - DialogTitle, +import { + Dialog, + DialogSurface, + DialogBody, + DialogTitle, DialogContent, DialogActions, - Button + Button } from "@fluentui/react-components"; import { Dismiss24Regular } from "@fluentui/react-icons"; import Lottie from 'lottie-react'; @@ -39,24 +39,26 @@ const ProgressModal: React.FC = ({ migrationError = false, onNavigateHome }) => { - // Calculate progress percentage based on phases + // Calculate progress percentage based on step (stable step-level identifier) const getProgressPercentage = () => { if (migrationError) return 0; // Show 0% progress for errors - if (!apiData || !apiData.phase) return 0; - - const phases = ['Analysis', 'Design', 'YAML', 'Documentation']; - const currentPhaseIndex = phases.indexOf(apiData.phase); - - if (currentPhaseIndex === -1) return 0; if (processingCompleted && !migrationError) return 100; - - // Each phase represents 25% of the progress - const baseProgress = (currentPhaseIndex / phases.length) * 100; - - // Add some progress within the current phase based on time elapsed - const phaseProgress = Math.min(20, (currentPhaseIndex + 1) * 5); - - return Math.min(95, baseProgress + phaseProgress); + if (!apiData) return 0; + + // Use apiData.step (stable: "analysis", "design", "yaml_conversion", "documentation") + // rather than apiData.phase which changes to sub-phase names like "Platform Enhancement" + const steps = ['analysis', 'design', 'yaml_conversion', 'documentation']; + const currentStepIndex = steps.indexOf((apiData.step || '').toLowerCase()); + + if (currentStepIndex === -1) return 0; + + // Each step represents 25% of the progress + const baseProgress = (currentStepIndex / steps.length) * 100; + + // Add some progress within the current step + const stepProgress = Math.min(20, (currentStepIndex + 1) * 5); + + return Math.min(95, baseProgress + stepProgress); }; const progressPercentage = getProgressPercentage(); @@ -75,8 +77,8 @@ const ProgressModal: React.FC = ({ }; return ( - { // Just close the modal without triggering onCancel setOpen(data.open); @@ -88,9 +90,9 @@ const ProgressModal: React.FC = ({
{title} {!processingCompleted && ( -
- +
{/* Current Phase Display */}
{!processingCompleted ? ( - ) : ( -
= ({
- {migrationError ? 'Migration Failed!' : - processingCompleted ? 'Migration Completed!' : + {migrationError ? 'Migration Failed!' : + processingCompleted ? 'Migration Completed!' : `${currentPhase || 'Processing'} Phase`}
{migrationError ? 'The migration stopped before completion.' : - processingCompleted ? 'Your container migration is ready!' : + processingCompleted ? 'Your container migration is ready!' : 'Converting your container workloads...'}
@@ -143,9 +145,9 @@ const ProgressModal: React.FC = ({ {/* Progress Bar */}
-
@@ -163,7 +165,7 @@ const ProgressModal: React.FC = ({ style={{ width: `${progressPercentage}%`, height: '100%', - backgroundColor: migrationError ? '#dc3545' : + backgroundColor: migrationError ? '#dc3545' : processingCompleted ? '#4CAF50' : '#0078d4', borderRadius: '4px', transition: 'width 0.5s ease-in-out' @@ -174,18 +176,229 @@ const ProgressModal: React.FC = ({ {/* Phase Information */} {apiData && ( -
-
Current Activity:
-
- {apiData.agents?.find(agent => agent.includes('speaking') || agent.includes('thinking')) || - `Working on ${currentPhase?.toLowerCase()} phase...`} +
+ Current Activity: + {(() => { + // Show step-level elapsed time from step_timings + const stepTimings = apiData.step_timings || {}; + const currentStep = (apiData.step || '').toLowerCase(); + const timing = stepTimings[currentStep]; + if (timing?.started_at) { + try { + const started = new Date(timing.started_at.replace(' UTC', 'Z')); + const diffSec = Math.max(0, Math.floor((Date.now() - started.getTime()) / 1000)); + let elapsed = ''; + if (diffSec < 60) elapsed = `${diffSec}s`; + else if (diffSec < 3600) elapsed = `${Math.floor(diffSec / 60)}m ${diffSec % 60}s`; + else elapsed = `${Math.floor(diffSec / 3600)}h ${Math.floor((diffSec % 3600) / 60)}m`; + return ( + + ⏱ {elapsed} + + ); + } catch { /* ignore */ } + } + return null; + })()}
- {apiData.active_agent_count && apiData.total_agents && ( + {(() => { + // Parse all active agents from raw telemetry strings + const activeAgents = (apiData.agents || []).filter((agent: string) => + agent.startsWith('✓') + ); + + if (activeAgents.length === 0) { + return ( +
+ Working on {currentPhase?.toLowerCase()} phase... +
+ ); + } + + return activeAgents.map((raw: string, idx: number) => { + // Strip prefix: "✓[🤔🔥] " → "" + const cleaned = raw.replace(/^[✓✗]\[.*?\]\s*/, ''); + // Agent name: everything before first ":" + const colonIdx = cleaned.indexOf(':'); + const agentName = colonIdx > 0 ? cleaned.substring(0, colonIdx).trim() : 'Agent'; + + // Determine action from status keywords + const actionIcons: Record = { + 'speaking': { icon: '🗣️', label: 'Speaking' }, + 'thinking': { icon: '💭', label: 'Thinking' }, + 'using_tool':{ icon: '🔧', label: 'Invoking Tool' }, + 'analyzing': { icon: '🔍', label: 'Analyzing' }, + 'responded': { icon: '✅', label: 'Responded' }, + 'ready': { icon: '⏳', label: 'Ready' }, + }; + const statusPart = colonIdx > 0 ? cleaned.substring(colonIdx + 1) : cleaned; + let actionInfo = { icon: '⚡', label: 'Working' }; + for (const [key, info] of Object.entries(actionIcons)) { + if (statusPart.toLowerCase().includes(key)) { + actionInfo = info; + break; + } + } + + // Extract tool name(s) from 🔧 segment + const toolMatch = raw.match(/🔧\s*([^|]+)/); + let toolName = ''; + if (toolMatch) { + // Clean up: take tool names, strip long JSON args + toolName = toolMatch[1] + .trim() + .replace(/\{[^}]*\}\.*/g, '') // remove JSON snippets + .replace(/\(.*?\)/g, '') // remove parenthesized args + .replace(/,\s*$/, '') + .trim(); + if (toolName) { + actionInfo = { icon: '🔧', label: 'Invoking Tool' }; + } + } + + // Extract action count from 📊 segment + const actionsMatch = raw.match(/📊\s*(\d+)\s*actions?/); + const actionCount = actionsMatch ? parseInt(actionsMatch[1]) : 0; + + // Extract blocking info from 🚧 segment + const blockingMatch = raw.match(/🚧\s*Blocking\s*(\d+)/); + const blockingCount = blockingMatch ? parseInt(blockingMatch[1]) : 0; + + // Special handling for Coordinator: parse routing info from message + let coordinatorTarget = ''; + let coordinatorInstruction = ''; + if (agentName === 'Coordinator') { + const agentData = apiData.agent_activities?.['Coordinator']; + // Try full message first (preview is truncated to 300 chars which breaks JSON parsing) + const fullMsg = agentData?.last_full_message || ''; + const preview = agentData?.last_message_preview || ''; + const msgToParse = fullMsg || preview; + let parsedInstruction = ''; + try { + const parsed = JSON.parse(msgToParse); + if (parsed.selected_participant) { + coordinatorTarget = parsed.selected_participant; + } + if (parsed.instruction) { + parsedInstruction = parsed.instruction; + coordinatorInstruction = parsedInstruction + .replace(/^Phase\s+\d+\s*:\s*.+?\s+-\s+/i, '') + .trim(); + } + // Dynamic badge based on instruction content + if (parsed.finish && parsed.instruction === 'complete') { + actionInfo = { icon: '✅', label: 'Completed' }; + } else if (parsed.finish && parsed.instruction === 'hard_blocked') { + actionInfo = { icon: '🚫', label: 'Blocked' }; + } else { + // Extract phase name for badge: "Phase X : Phase Title - ..." + const phaseMatch = parsedInstruction.match(/^Phase\s+\d+\s*:\s*(.+?)\s+-\s+/i); + if (phaseMatch) { + actionInfo = { icon: '⚡', label: phaseMatch[1].trim() }; + } else { + actionInfo = { icon: '⚡', label: 'Routing' }; + } + } + } catch { + actionInfo = { icon: '⚡', label: 'Routing' }; + const text = fullMsg || preview; + const nameMatch = text.match(/selected_participant['":\s]+([^'",$}]+)/); + if (nameMatch) coordinatorTarget = nameMatch[1].trim(); + const instrMatch = text.match(/instruction['":\s]+([^"]+?)(?:"|$)/); + if (instrMatch) { + coordinatorInstruction = instrMatch[1] + .replace(/^Phase\s+\d+\s*:\s*.+?\s+-\s+/i, '') + .trim(); + } + } + // Truncate long instructions for readability + if (coordinatorInstruction.length > 120) { + coordinatorInstruction = coordinatorInstruction.substring(0, 120) + '...'; + } + } + + return ( +
+ {/* Agent name + action */} +
+ {actionInfo.icon} + {agentName} + + {actionInfo.label} + +
+ {/* Coordinator routing target + instruction */} + {(coordinatorTarget || coordinatorInstruction) && ( +
+ {coordinatorTarget && → {coordinatorTarget}} + {coordinatorTarget && coordinatorInstruction && · } + {coordinatorInstruction && {coordinatorInstruction}} +
+ )} + {/* Tool + metrics row */} +
+ {toolName && ( + + 🔧 {toolName} + + )} + {actionCount > 0 && ( + + 📊 {actionCount} action{actionCount !== 1 ? 's' : ''} + + )} +
+ {/* Agent response preview (non-Coordinator, only when agent has responded) */} + {agentName !== 'Coordinator' && (() => { + const agentKey = agentName.replace(/\s+/g, '_'); + const agentData = apiData.agent_activities?.[agentKey]; + const currentAction = (agentData?.current_action || '').toLowerCase(); + // Only show response when agent has finished speaking/responded, not while thinking/invoking tools + if (currentAction !== 'speaking' && currentAction !== 'responded' && currentAction !== 'idle') return null; + const lastMsg = agentData?.last_message_preview || ''; + if (lastMsg && lastMsg.length > 20 && !lastMsg.startsWith('Ready for')) { + const truncated = lastMsg.length > 150 ? lastMsg.substring(0, 150) + '...' : lastMsg; + return ( +
+ 💬 {truncated} +
+ ); + } + return null; + })()} +
+ ); + }); + })()} + {apiData.active_agent_count != null && apiData.total_agents != null && (
{apiData.active_agent_count}/{apiData.total_agents} agents active {apiData.health_status?.includes('🟢') && ' 🟢'} @@ -196,16 +409,17 @@ const ProgressModal: React.FC = ({ {/* Recent Steps */} {phaseSteps.length > 0 && ( -
+
Recent Activity:
+
- {phaseSteps.slice(-5).map((step, index) => ( -
( +
= ({
))}
+
)}
- + {showCancelButton && !processingCompleted && ( - - )} - + {processingCompleted && ( {migrationError && onNavigateHome && ( - )} -