27 KiB
Executable File
MarketAlly.AIPlugin.DevOps - API Reference
Table of Contents
- Overview
- Plugin Interfaces
- Core Plugins
- Security Components
- Performance Components
- Core Infrastructure
- Data Models
- Error Handling
- Examples
Overview
The MarketAlly.AIPlugin.DevOps API provides a comprehensive suite of DevOps automation capabilities through a plugin-based architecture. All plugins implement the IAIPlugin interface and can be used independently or together through the AIPluginRegistry.
Core Namespace Structure
MarketAlly.AIPlugin.DevOps
├── Plugins/ # Core plugin implementations
├── Core/ # Base classes and infrastructure
├── Security/ # Security and audit components
├── Performance/ # Performance and optimization components
└── Models/ # Data models and result types
Plugin Interfaces
IAIPlugin Interface
All DevOps plugins implement the core IAIPlugin interface:
public interface IAIPlugin
{
IReadOnlyDictionary<string, Type> SupportedParameters { get; }
Task<AIPluginResult> ExecuteAsync(IReadOnlyDictionary<string, object> parameters);
}
BaseDevOpsPlugin Abstract Class
Enhanced base class providing common functionality:
public abstract class BaseDevOpsPlugin : IAIPlugin
{
// Common infrastructure
protected readonly ILogger _logger;
protected readonly AnalysisCache _cache;
protected readonly AuditLogger _auditLogger;
protected readonly RateLimiter _rateLimiter;
protected readonly CryptographicValidator _cryptoValidator;
// Abstract members
public abstract IReadOnlyDictionary<string, Type> SupportedParameters { get; }
protected abstract Task<AIPluginResult> ExecuteInternalAsync(IReadOnlyDictionary<string, object> parameters);
// Common functionality
protected async Task<bool> ValidateFileIntegrityAsync(string filePath, string expectedHash = null);
protected async Task LogSecurityIssueAsync(string issueType, string severity, string details = null);
protected bool IsFilePathSafe(string filePath);
protected async Task<T> GetOrSetCacheAsync<T>(string cacheKey, Func<Task<T>> factory, TimeSpan? expiry = null) where T : class;
}
Core Plugins
DevOpsScanPlugin
Comprehensive CI/CD pipeline analysis and security scanning.
Constructor
public DevOpsScanPlugin(ILogger<DevOpsScanPlugin> logger = null)
Supported Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
pipelinePath |
string | ✅ | - | Full path to pipeline configuration file or directory |
pipelineType |
string | ❌ | "auto" | Pipeline type: github, azure, jenkins, gitlab, auto |
checkSecurity |
bool | ❌ | true | Check for security vulnerabilities |
optimizeBuild |
bool | ❌ | true | Analyze build optimization opportunities |
checkBestPractices |
bool | ❌ | true | Check for best practices compliance |
generateRecommendations |
bool | ❌ | true | Generate optimization recommendations |
Usage Example
var plugin = new DevOpsScanPlugin(logger);
var result = await plugin.ExecuteAsync(new Dictionary<string, object>
{
["pipelinePath"] = ".github/workflows/ci.yml",
["pipelineType"] = "github",
["checkSecurity"] = true,
["optimizeBuild"] = true,
["checkBestPractices"] = true,
["generateRecommendations"] = true
});
Return Structure
{
Message = "DevOps pipeline scan completed",
PipelinePath = string,
PipelineType = string,
FilesAnalyzed = int,
SecurityIssues = List<SecurityIssue>,
OptimizationOpportunities = List<OptimizationOpportunity>,
BestPracticeViolations = List<BestPracticeViolation>,
Recommendations = List<string>,
Summary = {
TotalSecurityIssues = int,
TotalOptimizations = int,
TotalBestPracticeViolations = int,
OverallScore = int
}
}
Platform-Specific Features
GitHub Actions
- Workflow permission analysis
- Action version pinning validation
- Secret exposure detection
- Caching optimization analysis
Azure DevOps
- Variable security validation
- Service connection analysis
- Stage optimization recommendations
- VM image deprecation checks
GitLab CI
- Script injection vulnerability detection
- Modern syntax validation (rules vs only/except)
- Artifacts and caching analysis
- Container image security
DockerfileAnalyzerPlugin
Advanced Docker container analysis and optimization.
Constructor
public DockerfileAnalyzerPlugin(ILogger<DockerfileAnalyzerPlugin> logger = null)
Supported Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
dockerfilePath |
string | ✅ | - | Full path to the Dockerfile |
checkSecurity |
bool | ❌ | true | Check for security vulnerabilities |
optimizeSize |
bool | ❌ | true | Analyze image size optimization |
checkBestPractices |
bool | ❌ | true | Check for best practices |
checkMultiStage |
bool | ❌ | true | Validate multi-stage builds |
generateOptimized |
bool | ❌ | false | Generate optimized Dockerfile |
Usage Example
var plugin = new DockerfileAnalyzerPlugin(logger);
var result = await plugin.ExecuteAsync(new Dictionary<string, object>
{
["dockerfilePath"] = "./Dockerfile",
["checkSecurity"] = true,
["optimizeSize"] = true,
["checkBestPractices"] = true,
["checkMultiStage"] = true,
["generateOptimized"] = true
});
Return Structure
{
Message = "Dockerfile analysis completed",
DockerfilePath = string,
BaseImage = string,
TotalInstructions = int,
SecurityIssues = List<DockerSecurityIssue>,
SizeOptimizations = List<DockerSizeOptimization>,
BestPracticeViolations = List<DockerBestPracticeViolation>,
MultiStageAnalysis = DockerMultiStageAnalysis,
OptimizedDockerfile = string, // Only if generateOptimized = true
Summary = {
SecurityScore = int,
OptimizationScore = int,
BestPracticeScore = int,
OverallScore = int
}
}
Security Analysis Features
- Hardcoded secret detection
- Root user execution analysis
- ADD vs COPY security implications
- Package manager cache analysis
- Base image vulnerability assessment
Optimization Features
- Layer consolidation opportunities
- Multi-stage build efficiency
- Base image size recommendations
- .dockerignore validation
ConfigurationAnalyzerPlugin
Configuration management and environment validation.
Constructor
public ConfigurationAnalyzerPlugin(ILogger<ConfigurationAnalyzerPlugin> logger = null)
Supported Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
configDirectory |
string | ✅ | - | Full path to configuration directory |
filePatterns |
string | ❌ | ".json,.yaml,.yml,.xml,*.config" | File patterns to analyze |
checkDrift |
bool | ❌ | true | Check for configuration drift |
validateEnvironments |
bool | ❌ | true | Validate environment-specific settings |
checkSettings |
bool | ❌ | true | Check for missing/deprecated settings |
generateDocumentation |
bool | ❌ | false | Generate configuration documentation |
Usage Example
var plugin = new ConfigurationAnalyzerPlugin(logger);
var result = await plugin.ExecuteAsync(new Dictionary<string, object>
{
["configDirectory"] = "./config",
["filePatterns"] = "*.json,*.yaml",
["checkDrift"] = true,
["validateEnvironments"] = true,
["checkSettings"] = true,
["generateDocumentation"] = true
});
Return Structure
{
Message = "Configuration analysis completed",
ConfigDirectory = string,
FilesAnalyzed = int,
ConfigurationDrift = List<ConfigurationDrift>,
MissingSettings = List<MissingSetting>,
DeprecatedSettings = List<DeprecatedSetting>,
EnvironmentIssues = List<EnvironmentIssue>,
ConfigurationIssues = List<ConfigurationIssue>,
SecurityIssues = List<ConfigurationSecurityIssue>,
Documentation = string, // Only if generateDocumentation = true
Summary = {
TotalIssues = int,
DriftDetected = bool,
MissingSettingsCount = int,
SecurityIssuesCount = int,
OverallScore = int
}
}
Analysis Features
- Environment drift detection
- Secret scanning across configurations
- Deprecated pattern identification
- Consistency validation
- Environment-specific hardcoding detection
PipelineOptimizerPlugin
Build and deployment performance optimization.
Constructor
public PipelineOptimizerPlugin(ILogger<PipelineOptimizerPlugin> logger = null)
Supported Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
pipelineConfig |
string | ✅ | - | Full path to pipeline configuration |
optimizeBuildTime |
bool | ❌ | true | Analyze build time optimization |
checkParallelization |
bool | ❌ | true | Check for parallel execution opportunities |
analyzeResources |
bool | ❌ | true | Analyze resource utilization |
checkUnnecessarySteps |
bool | ❌ | true | Check for unnecessary steps |
generateOptimized |
bool | ❌ | false | Generate optimized pipeline |
Return Structure
{
Message = "Pipeline optimization completed",
PipelineConfig = string,
PipelineType = string,
OriginalMetrics = {
JobCount = int,
StepCount = int,
EstimatedBuildTime = string,
ParallelJobs = int
},
BuildTimeOptimizations = List<BuildTimeOptimization>,
ParallelizationOpportunities = List<ParallelizationOpportunity>,
ResourceOptimizations = List<ResourceOptimization>,
UnnecessarySteps = List<UnnecessaryStep>,
OptimizedConfig = string, // Only if generateOptimized = true
PerformanceMetrics = PerformanceMetrics,
Summary = {
TotalOptimizations = int,
EstimatedTimeSaving = string,
EstimatedCostSaving = string,
OptimizationScore = int
}
}
ChangelogGeneratorPlugin
Automated changelog generation from git history.
Constructor
public ChangelogGeneratorPlugin(ILogger<ChangelogGeneratorPlugin> logger = null)
Supported Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
repositoryPath |
string | ✅ | - | Full path to git repository |
fromVersion |
string | ❌ | null | Starting version or tag |
toVersion |
string | ❌ | "HEAD" | Ending version or tag |
format |
string | ❌ | "markdown" | Output format: markdown, json, html |
groupByType |
bool | ❌ | true | Group changes by type |
includeAuthors |
bool | ❌ | true | Include commit authors |
outputPath |
string | ❌ | null | Output file path |
Return Structure
{
Message = "Changelog generation completed",
RepositoryPath = string,
VersionRange = string,
CommitsProcessed = int,
ChangelogContent = string,
OutputPath = string,
Summary = {
Features = int,
Fixes = int,
BreakingChanges = int,
OtherChanges = int,
UniqueAuthors = int,
DateRange = string
}
}
Security Components
AuditLogger
Comprehensive security event logging and tracking.
Constructor
public AuditLogger(ILogger<AuditLogger> logger = null)
Methods
LogSecurityEventAsync
public async Task LogSecurityEventAsync(SecurityAuditEvent auditEvent)
LogAnalysisEventAsync
public async Task LogAnalysisEventAsync(string pluginName, string filePath, int issuesFound, TimeSpan analysisTime)
LogSecurityIssueAsync
public async Task LogSecurityIssueAsync(string pluginName, string filePath, string issueType, string severity)
SecurityAuditEvent Model
public class SecurityAuditEvent
{
public DateTime Timestamp { get; set; } = DateTime.UtcNow;
public SecurityEventType EventType { get; set; }
public SecuritySeverity Severity { get; set; }
public string Source { get; set; }
public string UserId { get; set; }
public string Details { get; set; }
public Dictionary<string, object> Metadata { get; set; } = new();
}
Event Types
public enum SecurityEventType
{
AnalysisStarted,
AnalysisCompleted,
SecurityIssueDetected,
ConfigurationValidated,
FileAccessed,
PermissionChecked,
CryptographicOperation
}
public enum SecuritySeverity
{
Low,
Medium,
High,
Critical
}
CryptographicValidator
File integrity and cryptographic validation services.
Constructor
public CryptographicValidator(AuditLogger auditLogger = null)
Methods
ValidateFileIntegrityAsync
public async Task<bool> ValidateFileIntegrityAsync(string filePath, string expectedHash = null)
ComputeFileHashAsync
public async Task<string> ComputeFileHashAsync(string filePath)
ValidateConfigurationSignatureAsync
public async Task<bool> ValidateConfigurationSignatureAsync(string configPath, string signaturePath)
ComputeContentSignatureAsync
public async Task<string> ComputeContentSignatureAsync(string content)
ValidateJsonIntegrityAsync
public async Task<bool> ValidateJsonIntegrityAsync(string jsonContent)
RateLimiter
Token bucket-based rate limiting for API protection.
Constructor
public RateLimiter(AuditLogger auditLogger = null)
Methods
TryExecuteAsync
public async Task<bool> TryExecuteAsync(string clientId, int tokensRequired = 1, int maxTokens = 100, TimeSpan? refillInterval = null)
ClearClient
public void ClearClient(string clientId)
ClearAll
public void ClearAll()
Performance Components
AnalysisCache
Intelligent caching system for analysis results.
Constructor
public AnalysisCache(ILogger<AnalysisCache> logger = null, TimeSpan? defaultExpiry = null)
Methods
GetOrSetAsync
public async Task<T> GetOrSetAsync<T>(string key, Func<Task<T>> factory, TimeSpan? expiry = null) where T : class
GetAsync
public async Task<T> GetAsync<T>(string key) where T : class
SetAsync
public async Task SetAsync<T>(string key, T value, TimeSpan? expiry = null)
GenerateFileBasedCacheKeyAsync
public async Task<string> GenerateFileBasedCacheKeyAsync(string filePath, string operation)
InvalidateByPattern
public void InvalidateByPattern(string pattern)
GetStatistics
public CacheStatistics GetStatistics()
Cache Statistics Model
public class CacheStatistics
{
public int TotalEntries { get; set; }
public int ValidEntries { get; set; }
public int ExpiredEntries { get; set; }
public double HitRate { get; set; }
}
ParallelAnalyzer
High-performance parallel processing for analysis operations.
Constructor
public ParallelAnalyzer<TInput, TResult>(int maxConcurrency = Environment.ProcessorCount, ILogger<ParallelAnalyzer<TInput, TResult>> logger = null)
Methods
AnalyzeAsync
public async Task<IList<TResult>> AnalyzeAsync(
IEnumerable<TInput> inputs,
Func<TInput, Task<TResult>> analyzer,
CancellationToken cancellationToken = default)
AnalyzeWithKeysAsync
public async Task<IDictionary<TInput, TResult>> AnalyzeWithKeysAsync(
IEnumerable<TInput> inputs,
Func<TInput, Task<TResult>> analyzer,
CancellationToken cancellationToken = default)
AnalyzeBatchAsync
public async Task<AnalysisBatch<TInput, TResult>> AnalyzeBatchAsync(
IEnumerable<TInput> inputs,
Func<TInput, Task<TResult>> analyzer,
int batchSize = 10,
CancellationToken cancellationToken = default)
Batch Analysis Result
public class AnalysisBatch<TInput, TResult>
{
public IList<TResult> Results { get; set; } = new List<TResult>();
public IList<AnalysisError<TInput>> Errors { get; set; } = new List<AnalysisError<TInput>>();
public int TotalProcessed { get; set; }
public int SuccessCount { get; set; }
public int ErrorCount { get; set; }
public double SuccessRate => TotalProcessed > 0 ? (double)SuccessCount / TotalProcessed * 100 : 0;
}
Data Models
Common Analysis Results
SecurityIssue
public class SecurityIssue
{
public string Severity { get; set; } // Critical, High, Medium, Low
public string Issue { get; set; }
public string Location { get; set; }
public string Recommendation { get; set; }
}
OptimizationOpportunity
public class OptimizationOpportunity
{
public string Type { get; set; }
public string Description { get; set; }
public string Location { get; set; }
public string Recommendation { get; set; }
public string EstimatedTimesSaving { get; set; }
}
BestPracticeViolation
public class BestPracticeViolation
{
public string Rule { get; set; }
public string Description { get; set; }
public string Location { get; set; }
public string Recommendation { get; set; }
}
Platform-Specific Models
GitHub Actions Models
public class GitHubWorkflow
{
public string Name { get; set; }
public Dictionary<string, object> On { get; set; }
public Dictionary<string, object> Permissions { get; set; }
public Dictionary<string, GitHubJob> Jobs { get; set; }
}
public class GitHubJob
{
public string Name { get; set; }
public string RunsOn { get; set; }
public int? TimeoutMinutes { get; set; }
public List<string> Needs { get; set; }
public GitHubStrategy Strategy { get; set; }
public List<GitHubStep> Steps { get; set; }
}
Azure DevOps Models
public class AzureDevOpsPipeline
{
public object Trigger { get; set; }
public AzurePool Pool { get; set; }
public Dictionary<string, object> Variables { get; set; }
public List<AzureStage> Stages { get; set; }
public List<AzureJob> Jobs { get; set; }
}
public class AzureStage
{
public string Stage { get; set; }
public string DisplayName { get; set; }
public string Condition { get; set; }
public List<string> DependsOn { get; set; }
public List<AzureJob> Jobs { get; set; }
}
GitLab CI Models
public class GitLabCIPipeline
{
public List<string> Stages { get; set; }
public Dictionary<string, object> Variables { get; set; }
public GitLabCache Cache { get; set; }
public string Image { get; set; }
public List<string> Services { get; set; }
public Dictionary<string, GitLabJob> Jobs { get; set; }
}
public class GitLabJob
{
public string Name { get; set; }
public string Stage { get; set; }
public string Image { get; set; }
public List<string> Script { get; set; }
public GitLabArtifacts Artifacts { get; set; }
public List<GitLabRule> Rules { get; set; }
}
Configuration Models
ConfigurationDrift
public class ConfigurationDrift
{
public string Key { get; set; }
public Dictionary<string, string> EnvironmentValues { get; set; } = new();
public string DriftType { get; set; }
public string Recommendation { get; set; }
}
Docker Models
public class DockerfileStructure
{
public List<DockerInstruction> Instructions { get; set; } = new List<DockerInstruction>();
}
public class DockerInstruction
{
public string Command { get; set; }
public string Arguments { get; set; }
public int LineNumber { get; set; }
public string OriginalLine { get; set; }
}
public class DockerMultiStageAnalysis
{
public bool IsMultiStage { get; set; }
public int StageCount { get; set; }
public List<DockerStage> Stages { get; set; } = new List<DockerStage>();
public List<string> Recommendations { get; set; } = new List<string>();
}
Error Handling
AIPluginResult
All plugin operations return an AIPluginResult:
public class AIPluginResult
{
public bool IsSuccess { get; set; }
public object Data { get; set; }
public Exception Error { get; set; }
public string Message { get; set; }
}
Common Error Scenarios
File Not Found
return new AIPluginResult(
new FileNotFoundException($"Pipeline path not found: {pipelinePath}"),
"Pipeline path not found"
);
Invalid Configuration
return new AIPluginResult(
new ArgumentException("Parameter validation failed"),
"Parameter validation failed"
);
Rate Limit Exceeded
return new AIPluginResult(
new InvalidOperationException("Rate limit exceeded"),
"Rate limit exceeded"
);
Validation Results
public class ValidationResult
{
public List<ValidationError> Errors { get; } = new();
public List<ValidationWarning> Warnings { get; } = new();
public bool IsValid => !Errors.Any();
public void AddError(string parameter, string message, string errorCode);
public void AddWarning(string parameter, string message, string warningCode);
}
Examples
Complete Integration Example
using MarketAlly.AIPlugin;
using MarketAlly.AIPlugin.DevOps.Plugins;
using MarketAlly.AIPlugin.DevOps.Security;
using MarketAlly.AIPlugin.DevOps.Performance;
using Microsoft.Extensions.Logging;
// Setup logging
var loggerFactory = LoggerFactory.Create(builder => builder.AddConsole());
var logger = loggerFactory.CreateLogger<DevOpsScanPlugin>();
// Create plugin registry
var registry = new AIPluginRegistry();
// Register all DevOps plugins
registry.RegisterPlugin(new DevOpsScanPlugin(logger));
registry.RegisterPlugin(new DockerfileAnalyzerPlugin(logger));
registry.RegisterPlugin(new ConfigurationAnalyzerPlugin(logger));
registry.RegisterPlugin(new PipelineOptimizerPlugin(logger));
registry.RegisterPlugin(new ChangelogGeneratorPlugin(logger));
// Comprehensive pipeline analysis
var pipelineAnalysis = await registry.CallFunctionAsync("DevOpsScan", new Dictionary<string, object>
{
["pipelinePath"] = ".github/workflows/ci.yml",
["pipelineType"] = "auto",
["checkSecurity"] = true,
["optimizeBuild"] = true,
["checkBestPractices"] = true,
["generateRecommendations"] = true
});
if (pipelineAnalysis.IsSuccess)
{
var data = pipelineAnalysis.Data as dynamic;
Console.WriteLine($"Pipeline Analysis Complete:");
Console.WriteLine($" Security Issues: {data.Summary.TotalSecurityIssues}");
Console.WriteLine($" Optimizations: {data.Summary.TotalOptimizations}");
Console.WriteLine($" Overall Score: {data.Summary.OverallScore}/100");
// Process security issues
foreach (var issue in data.SecurityIssues)
{
Console.WriteLine($"⚠️ {issue.Severity}: {issue.Issue}");
Console.WriteLine($" Location: {issue.Location}");
Console.WriteLine($" Fix: {issue.Recommendation}");
}
}
// Docker analysis
var dockerAnalysis = await registry.CallFunctionAsync("DockerfileAnalyzer", new Dictionary<string, object>
{
["dockerfilePath"] = "./Dockerfile",
["checkSecurity"] = true,
["optimizeSize"] = true,
["generateOptimized"] = true
});
// Configuration analysis
var configAnalysis = await registry.CallFunctionAsync("ConfigurationAnalyzer", new Dictionary<string, object>
{
["configDirectory"] = "./config",
["checkDrift"] = true,
["generateDocumentation"] = true
});
// Generate changelog
var changelogResult = await registry.CallFunctionAsync("ChangelogGenerator", new Dictionary<string, object>
{
["repositoryPath"] = ".",
["format"] = "markdown",
["outputPath"] = "./CHANGELOG.md"
});
Advanced Security Integration
using MarketAlly.AIPlugin.DevOps.Security;
// Setup comprehensive security monitoring
var auditLogger = new AuditLogger(logger);
var cryptoValidator = new CryptographicValidator(auditLogger);
var rateLimiter = new RateLimiter(auditLogger);
// Validate file integrity before analysis
var isValid = await cryptoValidator.ValidateFileIntegrityAsync("./Dockerfile");
if (!isValid)
{
await auditLogger.LogSecurityEventAsync(new SecurityAuditEvent
{
EventType = SecurityEventType.SecurityIssueDetected,
Severity = SecuritySeverity.High,
Source = "FileValidator",
Details = "File integrity check failed"
});
return;
}
// Rate limiting check
var clientId = "user@example.com";
var canProceed = await rateLimiter.TryExecuteAsync(clientId, tokensRequired: 5);
if (!canProceed)
{
Console.WriteLine("Rate limit exceeded. Please try again later.");
return;
}
// Proceed with analysis...
Performance Optimization Example
using MarketAlly.AIPlugin.DevOps.Performance;
// Setup high-performance analysis
var cache = new AnalysisCache(logger, TimeSpan.FromHours(1));
var parallelAnalyzer = new ParallelAnalyzer<string, AnalysisResult>(
maxConcurrency: Environment.ProcessorCount * 2,
logger
);
// Batch analysis of multiple files
var configFiles = Directory.GetFiles("./configs", "*.json", SearchOption.AllDirectories);
var results = await parallelAnalyzer.AnalyzeAsync(
configFiles,
async filePath =>
{
// Use caching for repeated analysis
var cacheKey = await cache.GenerateFileBasedCacheKeyAsync(filePath, "config-analysis");
return await cache.GetOrSetAsync(cacheKey, async () =>
{
var plugin = new ConfigurationAnalyzerPlugin(logger);
var result = await plugin.ExecuteAsync(new Dictionary<string, object>
{
["configDirectory"] = Path.GetDirectoryName(filePath),
["filePatterns"] = Path.GetFileName(filePath)
});
return result.Data;
});
}
);
Console.WriteLine($"Analyzed {results.Count} configuration files");
// Check cache performance
var stats = cache.GetStatistics();
Console.WriteLine($"Cache hit rate: {stats.HitRate:P2}");
Version Compatibility
| API Version | .NET Version | Package Version | Status |
|---|---|---|---|
| 2.1.x | .NET 8.0+ | 2.1.0+ | ✅ Current |
| 2.0.x | .NET 8.0+ | 2.0.0-2.0.x | ⚠️ Legacy |
| 1.x.x | .NET 6.0+ | 1.0.0-1.x.x | ❌ Deprecated |
Support & Resources
- Documentation: Complete Guide
- Examples: examples/
- Issues: GitHub Issues
- API Updates: CHANGELOG.md
API Reference last updated: 2025-06-24
Version: 2.1.0
Compatible with: .NET 8.0+