diff --git a/RoslynBridge.WebApi/Controllers/HealthController.cs b/RoslynBridge.WebApi/Controllers/HealthController.cs new file mode 100644 index 0000000..34e6a3b --- /dev/null +++ b/RoslynBridge.WebApi/Controllers/HealthController.cs @@ -0,0 +1,72 @@ +using Microsoft.AspNetCore.Mvc; +using RoslynBridge.WebApi.Models; +using RoslynBridge.WebApi.Services; + +namespace RoslynBridge.WebApi.Controllers; + +/// +/// Health check and status controller +/// +[ApiController] +[Route("api/[controller]")] +[Produces("application/json")] +public class HealthController : ControllerBase +{ + private readonly IRoslynBridgeClient _bridgeClient; + private readonly ILogger _logger; + + public HealthController(IRoslynBridgeClient bridgeClient, ILogger logger) + { + _bridgeClient = bridgeClient; + _logger = logger; + } + + /// + /// Get the health status of the middleware and Visual Studio plugin + /// + /// Cancellation token + /// Health status information + /// Service is healthy + /// Service is unhealthy + [HttpGet] + [ProducesResponseType(typeof(HealthCheckResponse), StatusCodes.Status200OK)] + [ProducesResponseType(typeof(HealthCheckResponse), StatusCodes.Status503ServiceUnavailable)] + public async Task> GetHealth(CancellationToken cancellationToken) + { + var response = new HealthCheckResponse(); + + try + { + var isVsPluginHealthy = await _bridgeClient.IsHealthyAsync(null, cancellationToken); + response.VsPluginStatus = isVsPluginHealthy ? "Connected" : "Disconnected"; + + if (!isVsPluginHealthy) + { + response.Status = "Degraded"; + _logger.LogWarning("Visual Studio plugin is not accessible"); + return StatusCode(StatusCodes.Status503ServiceUnavailable, response); + } + + _logger.LogInformation("Health check passed"); + return Ok(response); + } + catch (Exception ex) + { + _logger.LogError(ex, "Health check failed"); + response.Status = "Unhealthy"; + response.VsPluginStatus = "Error"; + return StatusCode(StatusCodes.Status503ServiceUnavailable, response); + } + } + + /// + /// Simple ping endpoint + /// + /// Pong response + [HttpGet("ping")] + [ProducesResponseType(typeof(object), StatusCodes.Status200OK)] + public IActionResult Ping() + { + return Ok(new { message = "pong", timestamp = DateTime.UtcNow }); + } +} diff --git a/RoslynBridge.WebApi/Controllers/HistoryController.cs b/RoslynBridge.WebApi/Controllers/HistoryController.cs new file mode 100644 index 0000000..a2995f1 --- /dev/null +++ b/RoslynBridge.WebApi/Controllers/HistoryController.cs @@ -0,0 +1,117 @@ +using Microsoft.AspNetCore.Mvc; +using RoslynBridge.WebApi.Models; +using RoslynBridge.WebApi.Services; + +namespace RoslynBridge.WebApi.Controllers; + +/// +/// Controller for accessing query history +/// +[ApiController] +[Route("api/[controller]")] +[Produces("application/json")] +public class HistoryController : ControllerBase +{ + private readonly IHistoryService _historyService; + private readonly ILogger _logger; + + public HistoryController(IHistoryService historyService, ILogger logger) + { + _historyService = historyService; + _logger = logger; + } + + /// + /// Get all history entries + /// + /// List of all history entries + /// Returns the list of history entries + [HttpGet] + [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] + public ActionResult> GetAll() + { + var entries = _historyService.GetAll(); + return Ok(entries); + } + + /// + /// Get a specific history entry by ID + /// + /// The history entry ID + /// The history entry + /// Returns the history entry + /// Entry not found + [HttpGet("{id}")] + [ProducesResponseType(typeof(QueryHistoryEntry), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public ActionResult GetById(string id) + { + var entry = _historyService.GetById(id); + if (entry == null) + { + return NotFound(new { message = $"History entry {id} not found" }); + } + + return Ok(entry); + } + + /// + /// Get recent history entries + /// + /// Number of entries to return (default: 50, max: 500) + /// List of recent history entries + /// Returns the list of recent entries + [HttpGet("recent")] + [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] + public ActionResult> GetRecent([FromQuery] int count = 50) + { + if (count > 500) count = 500; + if (count < 1) count = 1; + + var entries = _historyService.GetRecent(count); + return Ok(entries); + } + + /// + /// Get history statistics + /// + /// Statistics about history entries + [HttpGet("stats")] + [ProducesResponseType(typeof(object), StatusCodes.Status200OK)] + public ActionResult GetStats() + { + var entries = _historyService.GetAll(); + var stats = new + { + totalEntries = _historyService.GetCount(), + successfulRequests = entries.Count(e => e.Success), + failedRequests = entries.Count(e => !e.Success), + averageDurationMs = entries.Any() ? entries.Average(e => e.DurationMs) : 0, + oldestEntry = entries.Any() ? entries.Last().Timestamp : (DateTime?)null, + newestEntry = entries.Any() ? entries.First().Timestamp : (DateTime?)null, + topPaths = entries + .GroupBy(e => e.Path) + .OrderByDescending(g => g.Count()) + .Take(10) + .Select(g => new { path = g.Key, count = g.Count() }) + }; + + return Ok(stats); + } + + /// + /// Clear all history entries + /// + /// Confirmation message + /// History cleared successfully + [HttpDelete] + [ProducesResponseType(typeof(object), StatusCodes.Status200OK)] + public ActionResult Clear() + { + var count = _historyService.GetCount(); + _historyService.Clear(); + _logger.LogInformation("History cleared: {Count} entries removed", count); + + return Ok(new { message = $"History cleared: {count} entries removed" }); + } +} diff --git a/RoslynBridge.WebApi/Controllers/InstancesController.cs b/RoslynBridge.WebApi/Controllers/InstancesController.cs new file mode 100644 index 0000000..14cc80c --- /dev/null +++ b/RoslynBridge.WebApi/Controllers/InstancesController.cs @@ -0,0 +1,168 @@ +using Microsoft.AspNetCore.Mvc; +using RoslynBridge.WebApi.Models; +using RoslynBridge.WebApi.Services; + +namespace RoslynBridge.WebApi.Controllers; + +/// +/// Controller for managing Visual Studio instance registrations +/// +[ApiController] +[Route("api/[controller]")] +[Produces("application/json")] +public class InstancesController : ControllerBase +{ + private readonly IInstanceRegistryService _registryService; + private readonly ILogger _logger; + + public InstancesController( + IInstanceRegistryService registryService, + ILogger logger) + { + _registryService = registryService; + _logger = logger; + } + + /// + /// Register a new Visual Studio instance + /// + /// Instance information + /// Registration result + [HttpPost("register")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + public IActionResult Register([FromBody] VSInstanceInfo instance) + { + if (!ModelState.IsValid) + { + return BadRequest(ModelState); + } + + _registryService.Register(instance); + + return Ok(new + { + success = true, + message = "Instance registered successfully", + processId = instance.ProcessId, + port = instance.Port + }); + } + + /// + /// Unregister a Visual Studio instance + /// + /// Process ID of the instance to unregister + /// Unregistration result + [HttpPost("unregister/{processId}")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public IActionResult Unregister(int processId) + { + var removed = _registryService.Unregister(processId); + + if (!removed) + { + return NotFound(new { success = false, message = "Instance not found" }); + } + + return Ok(new { success = true, message = "Instance unregistered successfully" }); + } + + /// + /// Update heartbeat for a Visual Studio instance + /// + /// Process ID of the instance + /// Heartbeat update result + [HttpPost("heartbeat/{processId}")] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public IActionResult Heartbeat(int processId) + { + var updated = _registryService.UpdateHeartbeat(processId); + + if (!updated) + { + return NotFound(new { success = false, message = "Instance not found" }); + } + + return Ok(new { success = true, message = "Heartbeat updated" }); + } + + /// + /// Get all registered Visual Studio instances + /// + /// List of registered instances + [HttpGet] + [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)] + public IActionResult GetAll() + { + var instances = _registryService.GetAllInstances(); + return Ok(instances); + } + + /// + /// Get instance by process ID + /// + /// Process ID + /// Instance information + [HttpGet("by-pid/{processId}")] + [ProducesResponseType(typeof(VSInstanceInfo), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public IActionResult GetByProcessId(int processId) + { + var instance = _registryService.GetByProcessId(processId); + + if (instance == null) + { + return NotFound(new { success = false, message = "Instance not found" }); + } + + return Ok(instance); + } + + /// + /// Get instance by solution path + /// + /// Solution file path + /// Instance information + [HttpGet("by-solution")] + [ProducesResponseType(typeof(VSInstanceInfo), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public IActionResult GetBySolutionPath([FromQuery] string solutionPath) + { + if (string.IsNullOrEmpty(solutionPath)) + { + return BadRequest(new { success = false, message = "Solution path is required" }); + } + + var instance = _registryService.GetBySolutionPath(solutionPath); + + if (instance == null) + { + return NotFound(new { success = false, message = "No instance found for this solution" }); + } + + return Ok(instance); + } + + /// + /// Get instance by port + /// + /// Port number + /// Instance information + [HttpGet("by-port/{port}")] + [ProducesResponseType(typeof(VSInstanceInfo), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public IActionResult GetByPort(int port) + { + var instance = _registryService.GetByPort(port); + + if (instance == null) + { + return NotFound(new { success = false, message = "Instance not found" }); + } + + return Ok(instance); + } +} diff --git a/RoslynBridge.WebApi/Controllers/RoslynController.cs b/RoslynBridge.WebApi/Controllers/RoslynController.cs new file mode 100644 index 0000000..dc4c630 --- /dev/null +++ b/RoslynBridge.WebApi/Controllers/RoslynController.cs @@ -0,0 +1,283 @@ +using Microsoft.AspNetCore.Mvc; +using RoslynBridge.WebApi.Models; +using RoslynBridge.WebApi.Services; + +namespace RoslynBridge.WebApi.Controllers; + +/// +/// Controller for Roslyn code analysis operations +/// +[ApiController] +[Route("api/[controller]")] +[Produces("application/json")] +public class RoslynController : ControllerBase +{ + private readonly IRoslynBridgeClient _bridgeClient; + private readonly ILogger _logger; + + public RoslynController(IRoslynBridgeClient bridgeClient, ILogger logger) + { + _bridgeClient = bridgeClient; + _logger = logger; + } + + /// + /// Execute a Roslyn query + /// + /// The query request + /// Optional: specific VS instance port to target + /// Cancellation token + /// The query result + /// Query executed successfully + /// Invalid request + /// Internal server error + [HttpPost("query")] + [ProducesResponseType(typeof(RoslynQueryResponse), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status500InternalServerError)] + public async Task> ExecuteQuery( + [FromBody] RoslynQueryRequest request, + [FromQuery] int? instancePort = null, + CancellationToken cancellationToken = default) + { + if (!ModelState.IsValid) + { + return BadRequest(ModelState); + } + + _logger.LogInformation("Received query request: {QueryType}", request.QueryType); + + var result = await _bridgeClient.ExecuteQueryAsync(request, instancePort, cancellationToken); + + if (!result.Success) + { + _logger.LogWarning("Query failed: {Error}", result.Error); + } + + return Ok(result); + } + + /// + /// Get all projects in the solution + /// + /// Optional: specific VS instance port to target + /// Cancellation token + /// List of projects + [HttpGet("projects")] + [ProducesResponseType(typeof(RoslynQueryResponse), StatusCodes.Status200OK)] + public async Task> GetProjects( + [FromQuery] int? instancePort = null, + CancellationToken cancellationToken = default) + { + var request = new RoslynQueryRequest { QueryType = "getprojects" }; + var result = await _bridgeClient.ExecuteQueryAsync(request, instancePort, cancellationToken); + return Ok(result); + } + + /// + /// Get solution overview + /// + /// Optional: specific VS instance port to target + /// Cancellation token + /// Solution statistics and overview + [HttpGet("solution/overview")] + [ProducesResponseType(typeof(RoslynQueryResponse), StatusCodes.Status200OK)] + public async Task> GetSolutionOverview( + [FromQuery] int? instancePort = null, + CancellationToken cancellationToken = default) + { + var request = new RoslynQueryRequest { QueryType = "getsolutionoverview" }; + var result = await _bridgeClient.ExecuteQueryAsync(request, instancePort, cancellationToken); + return Ok(result); + } + + /// + /// Get diagnostics (errors and warnings) + /// + /// Optional file path to filter diagnostics + /// Optional: specific VS instance port to target + /// Cancellation token + /// List of diagnostics + [HttpGet("diagnostics")] + [ProducesResponseType(typeof(RoslynQueryResponse), StatusCodes.Status200OK)] + public async Task> GetDiagnostics( + [FromQuery] string? filePath = null, + [FromQuery] int? instancePort = null, + CancellationToken cancellationToken = default) + { + var request = new RoslynQueryRequest + { + QueryType = "getdiagnostics", + FilePath = filePath + }; + var result = await _bridgeClient.ExecuteQueryAsync(request, instancePort, cancellationToken); + return Ok(result); + } + + /// + /// Get symbol information at a specific position + /// + /// File path + /// Line number (1-based) + /// Column number (0-based) + /// Optional: specific VS instance port to target + /// Cancellation token + /// Symbol information + [HttpGet("symbol")] + [ProducesResponseType(typeof(RoslynQueryResponse), StatusCodes.Status200OK)] + public async Task> GetSymbol( + [FromQuery] string filePath, + [FromQuery] int line, + [FromQuery] int column, + [FromQuery] int? instancePort = null, + CancellationToken cancellationToken = default) + { + var request = new RoslynQueryRequest + { + QueryType = "getsymbol", + FilePath = filePath, + Line = line, + Column = column + }; + var result = await _bridgeClient.ExecuteQueryAsync(request, instancePort, cancellationToken); + return Ok(result); + } + + /// + /// Find all references to a symbol + /// + /// File path + /// Line number (1-based) + /// Column number (0-based) + /// Optional: specific VS instance port to target + /// Cancellation token + /// List of references + [HttpGet("references")] + [ProducesResponseType(typeof(RoslynQueryResponse), StatusCodes.Status200OK)] + public async Task> FindReferences( + [FromQuery] string filePath, + [FromQuery] int line, + [FromQuery] int column, + [FromQuery] int? instancePort = null, + CancellationToken cancellationToken = default) + { + var request = new RoslynQueryRequest + { + QueryType = "findreferences", + FilePath = filePath, + Line = line, + Column = column + }; + var result = await _bridgeClient.ExecuteQueryAsync(request, instancePort, cancellationToken); + return Ok(result); + } + + /// + /// Search for symbols by name + /// + /// Symbol name or pattern + /// Optional symbol kind filter + /// Optional: specific VS instance port to target + /// Cancellation token + /// List of matching symbols + [HttpGet("symbol/search")] + [ProducesResponseType(typeof(RoslynQueryResponse), StatusCodes.Status200OK)] + public async Task> FindSymbol( + [FromQuery] string symbolName, + [FromQuery] string? kind = null, + [FromQuery] int? instancePort = null, + CancellationToken cancellationToken = default) + { + var request = new RoslynQueryRequest + { + QueryType = "findsymbol", + SymbolName = symbolName + }; + + if (!string.IsNullOrEmpty(kind)) + { + request.Parameters = new Dictionary { ["kind"] = kind }; + } + + var result = await _bridgeClient.ExecuteQueryAsync(request, instancePort, cancellationToken); + return Ok(result); + } + + /// + /// Format a document + /// + /// File path to format + /// Optional: specific VS instance port to target + /// Cancellation token + /// Format operation result + [HttpPost("format")] + [ProducesResponseType(typeof(RoslynQueryResponse), StatusCodes.Status200OK)] + public async Task> FormatDocument( + [FromBody] string filePath, + [FromQuery] int? instancePort = null, + CancellationToken cancellationToken = default) + { + var request = new RoslynQueryRequest + { + QueryType = "formatdocument", + FilePath = filePath + }; + var result = await _bridgeClient.ExecuteQueryAsync(request, instancePort, cancellationToken); + return Ok(result); + } + + /// + /// Add a NuGet package to a project + /// + /// Project name + /// NuGet package name + /// Optional package version + /// Optional: specific VS instance port to target + /// Cancellation token + /// Operation result + [HttpPost("project/package/add")] + [ProducesResponseType(typeof(RoslynQueryResponse), StatusCodes.Status200OK)] + public async Task> AddNuGetPackage( + [FromQuery] string projectName, + [FromQuery] string packageName, + [FromQuery] string? version = null, + [FromQuery] int? instancePort = null, + CancellationToken cancellationToken = default) + { + var request = new RoslynQueryRequest + { + QueryType = "addnugetpackage", + ProjectName = projectName, + PackageName = packageName, + Version = version + }; + var result = await _bridgeClient.ExecuteQueryAsync(request, instancePort, cancellationToken); + return Ok(result); + } + + /// + /// Build a project + /// + /// Project name + /// Build configuration (Debug/Release) + /// Optional: specific VS instance port to target + /// Cancellation token + /// Build result + [HttpPost("project/build")] + [ProducesResponseType(typeof(RoslynQueryResponse), StatusCodes.Status200OK)] + public async Task> BuildProject( + [FromQuery] string projectName, + [FromQuery] string? configuration = null, + [FromQuery] int? instancePort = null, + CancellationToken cancellationToken = default) + { + var request = new RoslynQueryRequest + { + QueryType = "buildproject", + ProjectName = projectName, + Configuration = configuration + }; + var result = await _bridgeClient.ExecuteQueryAsync(request, instancePort, cancellationToken); + return Ok(result); + } +} diff --git a/RoslynBridge.WebApi/Middleware/HistoryMiddleware.cs b/RoslynBridge.WebApi/Middleware/HistoryMiddleware.cs new file mode 100644 index 0000000..04aba08 --- /dev/null +++ b/RoslynBridge.WebApi/Middleware/HistoryMiddleware.cs @@ -0,0 +1,132 @@ +using System.Diagnostics; +using System.Text; +using System.Text.Json; +using RoslynBridge.WebApi.Models; +using RoslynBridge.WebApi.Services; + +namespace RoslynBridge.WebApi.Middleware; + +/// +/// Middleware to capture and log all Roslyn API requests and responses +/// +public class HistoryMiddleware +{ + private readonly RequestDelegate _next; + private readonly ILogger _logger; + + public HistoryMiddleware(RequestDelegate next, ILogger logger) + { + _next = next; + _logger = logger; + } + + public async Task InvokeAsync(HttpContext context, IHistoryService historyService) + { + // Only track Roslyn API endpoints + if (!context.Request.Path.StartsWithSegments("/api/roslyn")) + { + await _next(context); + return; + } + + var stopwatch = Stopwatch.StartNew(); + var historyEntry = new QueryHistoryEntry + { + Timestamp = DateTime.UtcNow, + Path = context.Request.Path, + Method = context.Request.Method, + ClientIp = context.Connection.RemoteIpAddress?.ToString() + }; + + // Capture request + RoslynQueryRequest? request = null; + if (context.Request.Method == "POST" && context.Request.ContentLength > 0) + { + context.Request.EnableBuffering(); + using var reader = new StreamReader(context.Request.Body, Encoding.UTF8, leaveOpen: true); + var requestBody = await reader.ReadToEndAsync(); + context.Request.Body.Position = 0; + + try + { + request = JsonSerializer.Deserialize(requestBody, new JsonSerializerOptions + { + PropertyNameCaseInsensitive = true + }); + historyEntry.Request = request; + } + catch (Exception ex) + { + _logger.LogWarning(ex, "Failed to deserialize request for history"); + } + } + + // Capture response + var originalBodyStream = context.Response.Body; + using var responseBody = new MemoryStream(); + context.Response.Body = responseBody; + + try + { + await _next(context); + + stopwatch.Stop(); + historyEntry.DurationMs = stopwatch.ElapsedMilliseconds; + historyEntry.Success = context.Response.StatusCode < 400; + + // Read response + responseBody.Seek(0, SeekOrigin.Begin); + var responseText = await new StreamReader(responseBody).ReadToEndAsync(); + responseBody.Seek(0, SeekOrigin.Begin); + + try + { + var response = JsonSerializer.Deserialize(responseText, new JsonSerializerOptions + { + PropertyNameCaseInsensitive = true + }); + historyEntry.Response = response; + } + catch (Exception ex) + { + _logger.LogWarning(ex, "Failed to deserialize response for history"); + } + + // Copy response back + await responseBody.CopyToAsync(originalBodyStream); + } + catch (Exception ex) + { + stopwatch.Stop(); + historyEntry.DurationMs = stopwatch.ElapsedMilliseconds; + historyEntry.Success = false; + _logger.LogError(ex, "Error in request processing"); + throw; + } + finally + { + context.Response.Body = originalBodyStream; + + // Add to history + try + { + historyService.Add(historyEntry); + } + catch (Exception ex) + { + _logger.LogError(ex, "Failed to add entry to history"); + } + } + } +} + +/// +/// Extension methods for adding history middleware +/// +public static class HistoryMiddlewareExtensions +{ + public static IApplicationBuilder UseHistoryTracking(this IApplicationBuilder builder) + { + return builder.UseMiddleware(); + } +} diff --git a/RoslynBridge.WebApi/Models/HealthCheckResponse.cs b/RoslynBridge.WebApi/Models/HealthCheckResponse.cs new file mode 100644 index 0000000..acd9d23 --- /dev/null +++ b/RoslynBridge.WebApi/Models/HealthCheckResponse.cs @@ -0,0 +1,32 @@ +namespace RoslynBridge.WebApi.Models; + +/// +/// Health check response for service status +/// +public class HealthCheckResponse +{ + /// + /// Overall health status + /// + public string Status { get; set; } = "Healthy"; + + /// + /// Web API service status + /// + public string WebApiStatus { get; set; } = "Running"; + + /// + /// Visual Studio plugin connection status + /// + public string VsPluginStatus { get; set; } = "Unknown"; + + /// + /// Timestamp of the health check + /// + public DateTime Timestamp { get; set; } = DateTime.UtcNow; + + /// + /// API version + /// + public string Version { get; set; } = "1.0.0"; +} diff --git a/RoslynBridge.WebApi/Models/QueryHistoryEntry.cs b/RoslynBridge.WebApi/Models/QueryHistoryEntry.cs new file mode 100644 index 0000000..e2bb6d2 --- /dev/null +++ b/RoslynBridge.WebApi/Models/QueryHistoryEntry.cs @@ -0,0 +1,52 @@ +namespace RoslynBridge.WebApi.Models; + +/// +/// Represents a single query history entry with request and response information +/// +public class QueryHistoryEntry +{ + /// + /// Unique identifier for this history entry + /// + public string Id { get; set; } = Guid.NewGuid().ToString(); + + /// + /// Timestamp when the request was received + /// + public DateTime Timestamp { get; set; } = DateTime.UtcNow; + + /// + /// The endpoint path that was called + /// + public string Path { get; set; } = string.Empty; + + /// + /// HTTP method used + /// + public string Method { get; set; } = string.Empty; + + /// + /// The Roslyn query request + /// + public RoslynQueryRequest? Request { get; set; } + + /// + /// The Roslyn query response + /// + public RoslynQueryResponse? Response { get; set; } + + /// + /// Request duration in milliseconds + /// + public long DurationMs { get; set; } + + /// + /// Whether the request was successful + /// + public bool Success { get; set; } + + /// + /// Client IP address + /// + public string? ClientIp { get; set; } +} diff --git a/RoslynBridge.WebApi/Models/RoslynQueryRequest.cs b/RoslynBridge.WebApi/Models/RoslynQueryRequest.cs new file mode 100644 index 0000000..e2fcb99 --- /dev/null +++ b/RoslynBridge.WebApi/Models/RoslynQueryRequest.cs @@ -0,0 +1,65 @@ +using System.ComponentModel.DataAnnotations; + +namespace RoslynBridge.WebApi.Models; + +/// +/// Request model for Roslyn query operations +/// +public class RoslynQueryRequest +{ + /// + /// Type of query to execute (e.g., "getsymbol", "getdocument", "findreferences") + /// + [Required] + public string QueryType { get; set; } = string.Empty; + + /// + /// File path for file-based operations + /// + public string? FilePath { get; set; } + + /// + /// Symbol name for symbol-based operations + /// + public string? SymbolName { get; set; } + + /// + /// Line number (1-based) for position-based operations + /// + public int? Line { get; set; } + + /// + /// Column number (0-based) for position-based operations + /// + public int? Column { get; set; } + + /// + /// Additional parameters for the query + /// + public Dictionary? Parameters { get; set; } + + /// + /// Project name for project operations + /// + public string? ProjectName { get; set; } + + /// + /// Package name for NuGet operations + /// + public string? PackageName { get; set; } + + /// + /// Version for NuGet package operations + /// + public string? Version { get; set; } + + /// + /// Build configuration (Debug/Release) + /// + public string? Configuration { get; set; } + + /// + /// Directory path for directory operations + /// + public string? DirectoryPath { get; set; } +} diff --git a/RoslynBridge.WebApi/Models/RoslynQueryResponse.cs b/RoslynBridge.WebApi/Models/RoslynQueryResponse.cs new file mode 100644 index 0000000..6f90d22 --- /dev/null +++ b/RoslynBridge.WebApi/Models/RoslynQueryResponse.cs @@ -0,0 +1,27 @@ +namespace RoslynBridge.WebApi.Models; + +/// +/// Response model for Roslyn query operations +/// +public class RoslynQueryResponse +{ + /// + /// Indicates whether the operation was successful + /// + public bool Success { get; set; } + + /// + /// Optional message providing additional context + /// + public string? Message { get; set; } + + /// + /// The response data (structure varies by query type) + /// + public object? Data { get; set; } + + /// + /// Error message if the operation failed + /// + public string? Error { get; set; } +} diff --git a/RoslynBridge.WebApi/Models/VSInstanceInfo.cs b/RoslynBridge.WebApi/Models/VSInstanceInfo.cs new file mode 100644 index 0000000..21b7ab6 --- /dev/null +++ b/RoslynBridge.WebApi/Models/VSInstanceInfo.cs @@ -0,0 +1,42 @@ +namespace RoslynBridge.WebApi.Models; + +/// +/// Information about a registered Visual Studio instance +/// +public class VSInstanceInfo +{ + /// + /// The port number where this VS instance is listening + /// + public int Port { get; set; } + + /// + /// The process ID of the Visual Studio instance + /// + public int ProcessId { get; set; } + + /// + /// The solution file path (if any solution is open) + /// + public string? SolutionPath { get; set; } + + /// + /// The solution name (if any solution is open) + /// + public string? SolutionName { get; set; } + + /// + /// When this instance was registered + /// + public DateTime RegisteredAt { get; set; } + + /// + /// Last heartbeat time + /// + public DateTime LastHeartbeat { get; set; } + + /// + /// List of project names in the solution + /// + public List Projects { get; set; } = new(); +} diff --git a/RoslynBridge.WebApi/Program.cs b/RoslynBridge.WebApi/Program.cs new file mode 100644 index 0000000..ab7e40e --- /dev/null +++ b/RoslynBridge.WebApi/Program.cs @@ -0,0 +1,129 @@ +using Microsoft.OpenApi.Models; +using RoslynBridge.WebApi.Middleware; +using RoslynBridge.WebApi.Services; +using System.Reflection; + +var builder = WebApplication.CreateBuilder(args); + +// Add Windows Service support +builder.Services.AddWindowsService(options => +{ + options.ServiceName = "RoslynBridge Web API"; +}); + +// Configure CORS +builder.Services.AddCors(options => +{ + options.AddPolicy("AllowAll", policy => + { + policy.AllowAnyOrigin() + .AllowAnyMethod() + .AllowAnyHeader(); + }); +}); + +// Add controllers +builder.Services.AddControllers(); + +// Register history service as singleton for in-memory storage +builder.Services.AddSingleton(); + +// Register instance registry service as singleton +builder.Services.AddSingleton(); + +// Register background service for cleaning up stale instances +builder.Services.AddHostedService(); + +// Configure HttpClient for Roslyn Bridge +var roslynBridgeUrl = builder.Configuration.GetValue("RoslynBridge:BaseUrl") ?? "http://localhost:59123"; +builder.Services.AddHttpClient(client => +{ + client.BaseAddress = new Uri(roslynBridgeUrl); + client.Timeout = TimeSpan.FromSeconds(30); +}); + +// Configure Swagger/OpenAPI +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(options => +{ + options.SwaggerDoc("v1", new OpenApiInfo + { + Title = "Roslyn Bridge Web API", + Version = "v1.0", + Description = "Modern web API middleware for Roslyn Bridge - connecting Claude AI to Visual Studio code analysis", + Contact = new OpenApiContact + { + Name = "Roslyn Bridge", + Url = new Uri("https://github.com/yourusername/roslynbridge") + } + }); + + // Include XML comments for better documentation + var xmlFilename = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"; + var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFilename); + if (File.Exists(xmlPath)) + { + options.IncludeXmlComments(xmlPath); + } + + // Add operation tags + options.TagActionsBy(api => + { + if (api.GroupName != null) + { + return new[] { api.GroupName }; + } + + if (api.ActionDescriptor is Microsoft.AspNetCore.Mvc.Controllers.ControllerActionDescriptor controllerActionDescriptor) + { + return new[] { controllerActionDescriptor.ControllerName }; + } + + return new[] { "Unknown" }; + }); +}); + +// Add health checks +builder.Services.AddHealthChecks(); + +var app = builder.Build(); + +// Configure the HTTP request pipeline +if (app.Environment.IsDevelopment()) +{ + app.UseDeveloperExceptionPage(); +} + +// Enable Swagger in all environments for easy access +app.UseSwagger(); +app.UseSwaggerUI(options => +{ + options.SwaggerEndpoint("/swagger/v1/swagger.json", "Roslyn Bridge API v1"); + options.RoutePrefix = string.Empty; // Serve Swagger UI at root + options.DocumentTitle = "Roslyn Bridge API"; + options.DisplayRequestDuration(); +}); + +app.UseHttpsRedirection(); + +// Enable CORS +app.UseCors("AllowAll"); + +// Enable history tracking middleware (must be before authorization and controllers) +app.UseHistoryTracking(); + +app.UseAuthorization(); + +// Map controllers +app.MapControllers(); + +// Map health check endpoint +app.MapHealthChecks("/health"); + +// Log startup information +var logger = app.Services.GetRequiredService>(); +logger.LogInformation("Roslyn Bridge Web API started"); +logger.LogInformation("Swagger UI available at: {Url}", app.Environment.IsDevelopment() ? "https://localhost:7001" : "/"); +logger.LogInformation("Connected to Roslyn Bridge at: {Url}", roslynBridgeUrl); + +app.Run(); diff --git a/RoslynBridge.WebApi/Properties/launchSettings.json b/RoslynBridge.WebApi/Properties/launchSettings.json new file mode 100644 index 0000000..09d7324 --- /dev/null +++ b/RoslynBridge.WebApi/Properties/launchSettings.json @@ -0,0 +1,38 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:43446", + "sslPort": 44347 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "http://localhost:5113", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:7070;http://localhost:5113", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/RoslynBridge.WebApi/README.md b/RoslynBridge.WebApi/README.md new file mode 100644 index 0000000..e92d82a --- /dev/null +++ b/RoslynBridge.WebApi/README.md @@ -0,0 +1,378 @@ +# Roslyn Bridge Web API + +A modern ASP.NET Core web API that acts as middleware between Claude AI and the Roslyn Bridge Visual Studio plugin, providing RESTful access to C# code analysis capabilities. + +## Overview + +This web API serves as a bridge between external clients (like Claude AI) and the Visual Studio Roslyn Bridge plugin. It provides: + +- **Modern RESTful API** with comprehensive Swagger/OpenAPI documentation +- **CORS-enabled** for web application access +- **Health monitoring** for both the web API and Visual Studio plugin connection +- **Simplified endpoints** for common Roslyn operations +- **Type-safe models** with validation + +## Architecture + +``` +┌─────────────┐ HTTP/REST ┌──────────────────┐ HTTP ┌─────────────────────┐ +│ Claude │ ◄─────────────────► │ Web API (5000) │ ◄────────────► │ VS Plugin (59123) │ +│ AI │ │ Middleware │ │ Roslyn Bridge │ +└─────────────┘ └──────────────────┘ └─────────────────────┘ +``` + +## Getting Started + +### Prerequisites + +- .NET 8.0 SDK or later +- Visual Studio with Roslyn Bridge plugin running (default port: 59123) + +### Quick Installation + +**Option 1: Automated Installation** + +Open PowerShell as Administrator: +```powershell +cd RoslynBridge.WebApi +.\install.ps1 -InstallService -StartService +``` + +This will build, publish, install as a Windows Service, and start the API automatically. + +For more detailed service setup options, see [SERVICE_SETUP.md](SERVICE_SETUP.md). + +**Option 2: Development Mode** + +1. **Start the Visual Studio plugin** (it should be running on port 59123) + +2. **Run the Web API:** + ```bash + cd RoslynBridge.WebApi + dotnet run + ``` + +3. **Access Swagger UI:** + - Navigate to: `http://localhost:5000` + - Or: `https://localhost:7001` (with HTTPS) + +### Configuration + +Edit `appsettings.json` to configure the connection: + +```json +{ + "RoslynBridge": { + "BaseUrl": "http://localhost:59123", + "TimeoutSeconds": 30 + } +} +``` + +## API Endpoints + +### Health Endpoints + +- **GET /api/health** - Check health status of Web API and VS plugin +- **GET /api/health/ping** - Simple ping endpoint + +### Roslyn Query Endpoints + +- **POST /api/roslyn/query** - Execute any Roslyn query +- **GET /api/roslyn/projects** - Get all projects in solution +- **GET /api/roslyn/solution/overview** - Get solution statistics +- **GET /api/roslyn/diagnostics** - Get errors and warnings +- **GET /api/roslyn/symbol** - Get symbol information at position +- **GET /api/roslyn/references** - Find all references to symbol +- **GET /api/roslyn/symbol/search** - Search for symbols by name + +### Refactoring Endpoints + +- **POST /api/roslyn/format** - Format a document +- **POST /api/roslyn/project/package/add** - Add NuGet package +- **POST /api/roslyn/project/build** - Build a project + +## Example Usage + +### Using curl + +```bash +# Health check +curl http://localhost:5000/api/health + +# Get all projects +curl http://localhost:5000/api/roslyn/projects + +# Get solution overview +curl http://localhost:5000/api/roslyn/solution/overview + +# Execute custom query +curl -X POST http://localhost:5000/api/roslyn/query \ + -H "Content-Type: application/json" \ + -d '{ + "queryType": "getsymbol", + "filePath": "C:\\path\\to\\file.cs", + "line": 10, + "column": 5 + }' + +# Search for symbols +curl "http://localhost:5000/api/roslyn/symbol/search?symbolName=MyClass" + +# Get diagnostics +curl "http://localhost:5000/api/roslyn/diagnostics" +``` + +### Using JavaScript/TypeScript + +```typescript +const response = await fetch('http://localhost:5000/api/roslyn/query', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + queryType: 'getprojects' + }) +}); + +const result = await response.json(); +console.log(result.data); +``` + +### Using C# + +```csharp +using var client = new HttpClient(); +client.BaseAddress = new Uri("http://localhost:5000"); + +var request = new RoslynQueryRequest +{ + QueryType = "getsolutionoverview" +}; + +var response = await client.PostAsJsonAsync("/api/roslyn/query", request); +var result = await response.Content.ReadFromJsonAsync(); +``` + +## Query Types + +The following query types are supported: + +### Code Analysis +- `getprojects` - Get all projects +- `getdocument` - Get document information +- `getsymbol` - Get symbol at position +- `getsemanticmodel` - Get semantic model +- `getsyntaxtree` - Get syntax tree +- `getdiagnostics` - Get compilation errors/warnings +- `findreferences` - Find all references +- `findsymbol` - Find symbols by name +- `gettypemembers` - Get type members +- `gettypehierarchy` - Get type hierarchy +- `findimplementations` - Find implementations +- `getnamespacetypes` - Get namespace types +- `getcallhierarchy` - Get call hierarchy +- `getsolutionoverview` - Get solution overview +- `getsymbolcontext` - Get symbol context +- `searchcode` - Search code patterns + +### Refactoring +- `formatdocument` - Format document +- `organizeusings` - Organize using statements +- `renamesymbol` - Rename symbol +- `addmissingusing` - Add missing using +- `applycodefix` - Apply code fix + +### Project Operations +- `addnugetpackage` - Add NuGet package +- `removenugetpackage` - Remove NuGet package +- `buildproject` - Build project +- `cleanproject` - Clean project +- `restorepackages` - Restore packages +- `createdirectory` - Create directory + +## Response Format + +All responses follow this structure: + +```json +{ + "success": true, + "message": "Optional message", + "data": { ... }, + "error": null +} +``` + +Error responses: + +```json +{ + "success": false, + "message": null, + "data": null, + "error": "Error description" +} +``` + +## Features + +### CORS Support +The API is configured with CORS enabled for all origins, making it accessible from web applications. + +### Swagger/OpenAPI +Comprehensive API documentation is available at the root URL (`/`) with: +- Detailed endpoint descriptions +- Request/response models +- Try-it-out functionality +- XML documentation comments + +### Health Checks +Built-in health checks monitor: +- Web API service status +- Visual Studio plugin connectivity +- Request/response timing + +### Logging +Structured logging with different levels: +- Information: General operations +- Warning: Non-critical issues +- Error: Failed operations +- Debug: Detailed tracing (Development only) + +## Development + +### Project Structure + +``` +RoslynBridge.WebApi/ +├── Controllers/ +│ ├── HealthController.cs # Health check endpoints +│ └── RoslynController.cs # Roslyn operation endpoints +├── Models/ +│ ├── RoslynQueryRequest.cs # Request DTOs +│ ├── RoslynQueryResponse.cs # Response DTOs +│ └── HealthCheckResponse.cs # Health check models +├── Services/ +│ ├── IRoslynBridgeClient.cs # Client interface +│ └── RoslynBridgeClient.cs # HTTP client implementation +├── Program.cs # Application configuration +├── appsettings.json # Configuration +└── README.md # This file +``` + +### Building + +```bash +dotnet build +``` + +### Running Tests + +```bash +dotnet test +``` + +### Publishing + +```bash +dotnet publish -c Release -o ./publish +``` + +## Deployment + +### Docker (Optional) + +Create a `Dockerfile`: + +```dockerfile +FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base +WORKDIR /app +EXPOSE 5000 + +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build +WORKDIR /src +COPY ["RoslynBridge.WebApi.csproj", "./"] +RUN dotnet restore +COPY . . +RUN dotnet build -c Release -o /app/build + +FROM build AS publish +RUN dotnet publish -c Release -o /app/publish + +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "RoslynBridge.WebApi.dll"] +``` + +Build and run: + +```bash +docker build -t roslyn-bridge-api . +docker run -p 5000:5000 roslyn-bridge-api +``` + +## Integration with Claude + +This API is designed to work seamlessly with Claude AI for code analysis tasks: + +1. Claude can query project structure +2. Analyze code for errors and warnings +3. Search for symbols and references +4. Suggest refactorings +5. Navigate code hierarchies + +Example Claude prompt: +``` +"Using the Roslyn Bridge API at http://localhost:5000, +analyze the current solution and identify any code quality issues." +``` + +## Troubleshooting + +### Visual Studio Plugin Not Connected + +Error: `{"vsPluginStatus": "Disconnected"}` + +**Solution:** +1. Ensure Visual Studio is running +2. Verify Roslyn Bridge plugin is installed and active +3. Check plugin is listening on port 59123 +4. Update `appsettings.json` if using a different port + +### CORS Errors + +If accessing from a web app: +- Verify CORS is enabled in `Program.cs` +- Check browser console for specific CORS errors +- Ensure the origin is allowed + +### Timeout Errors + +Increase timeout in `appsettings.json`: +```json +{ + "RoslynBridge": { + "TimeoutSeconds": 60 + } +} +``` + +## License + +This project is part of the Roslyn Bridge suite. + +## Contributing + +Contributions are welcome! Please: +1. Fork the repository +2. Create a feature branch +3. Make your changes +4. Submit a pull request + +## Support + +For issues and questions: +- Check the Swagger documentation at `/` +- Review the logs in the console output +- Verify the Visual Studio plugin is running diff --git a/RoslynBridge.WebApi/RoslynBridge.WebApi.csproj b/RoslynBridge.WebApi/RoslynBridge.WebApi.csproj new file mode 100644 index 0000000..35989f1 --- /dev/null +++ b/RoslynBridge.WebApi/RoslynBridge.WebApi.csproj @@ -0,0 +1,17 @@ + + + + net8.0 + enable + enable + true + $(NoWarn);1591 + + + + + + + + + diff --git a/RoslynBridge.WebApi/SERVICE_SETUP.md b/RoslynBridge.WebApi/SERVICE_SETUP.md new file mode 100644 index 0000000..75edda9 --- /dev/null +++ b/RoslynBridge.WebApi/SERVICE_SETUP.md @@ -0,0 +1,351 @@ +# Roslyn Bridge Web API - Windows Service Setup + +This guide explains how to install and run the Roslyn Bridge Web API as a Windows Service for always-on operation. + +## Prerequisites + +- **Administrator privileges** required for service installation +- **.NET 8.0 Runtime** or SDK installed +- **Visual Studio** with Roslyn Bridge extension must be running (the VS plugin on port 59123 is required) + +## Quick Start + +### Option 1: Automated Installation (Recommended) + +Open PowerShell **as Administrator** and run: + +```powershell +# Navigate to the project directory +cd C:\Path\To\RoslynBridge.WebApi + +# Complete installation (build, publish, install service, and start) +.\install.ps1 -InstallService -StartService +``` + +This single command will: +1. Check prerequisites (.NET SDK) +2. Restore NuGet packages +3. Build the project +4. Publish to `./publish` +5. Install as Windows Service +6. Start the service + +### Option 2: Manual Step-by-Step Installation + +#### 1. Publish the Application + +```powershell +# Navigate to the project directory +cd C:\Path\To\RoslynBridge.WebApi + +# Publish the application for Release +dotnet publish -c Release -o publish +``` + +This creates a self-contained deployment in the `publish` folder. + +#### 2. Install as Windows Service + +Open PowerShell **as Administrator**: + +```powershell +# Install the service +.\install-service.ps1 -Action Install + +# Start the service +.\install-service.ps1 -Action Start + +# Check service status +.\install-service.ps1 -Action Status +``` + +### 3. Verify Installation + +1. Open Windows Services (`services.msc`) +2. Look for "Roslyn Bridge Web API" +3. Verify it's running +4. Test the API: http://localhost:5000/api/health + +## Installation Script Options + +The `install.ps1` script provides several options for different scenarios: + +```powershell +# Full automated installation +.\install.ps1 -InstallService -StartService + +# Build and publish only (no service installation) +.\install.ps1 + +# Skip build, just publish and install +.\install.ps1 -SkipBuild -InstallService + +# Debug build instead of Release +.\install.ps1 -Configuration Debug + +# Custom publish path +.\install.ps1 -PublishPath "C:\MyApp\Publish" -InstallService + +# Reinstall after code changes +.\install.ps1 -InstallService +``` + +## Service Management Commands + +```powershell +# Install service +.\install-service.ps1 -Action Install + +# Start service +.\install-service.ps1 -Action Start + +# Stop service +.\install-service.ps1 -Action Stop + +# Restart service +.\install-service.ps1 -Action Restart + +# Check status +.\install-service.ps1 -Action Status + +# Uninstall service +.\install-service.ps1 -Action Uninstall +``` + +## Configuration + +### Service Settings + +The service is configured with: +- **Service Name**: `RoslynBridgeWebApi` +- **Display Name**: Roslyn Bridge Web API +- **Startup Type**: Automatic (starts with Windows) +- **Port**: 5000 (HTTP) + +### Changing the Port + +Edit `appsettings.json` before publishing: + +```json +{ + "Kestrel": { + "Endpoints": { + "Http": { + "Url": "http://localhost:YOUR_PORT" + } + } + } +} +``` + +### Logging + +When running as a service, logs are written to: +- **Windows Event Log**: Application → "Roslyn Bridge Web API" +- **Console**: Not available when running as service + +To view logs: +1. Open Event Viewer +2. Navigate to: Windows Logs → Application +3. Filter by source: "Roslyn Bridge Web API" + +## Troubleshooting + +### Service Won't Start + +**Symptom**: Service starts then immediately stops + +**Possible Causes**: +1. Port 5000 is already in use +2. Visual Studio plugin (port 59123) is not running +3. Missing .NET runtime + +**Solutions**: +```powershell +# Check if port 5000 is in use +netstat -ano | findstr :5000 + +# Check if VS plugin is accessible +curl -X POST http://localhost:59123/health -H "Content-Type: application/json" -d "{}" + +# Check Event Log for errors +Get-EventLog -LogName Application -Source "Roslyn Bridge Web API" -Newest 10 +``` + +### Service Installed but API Not Responding + +**Check the following**: +1. Service is running: `.\install-service.ps1 -Action Status` +2. Visual Studio is open with a solution loaded +3. Firewall isn't blocking port 5000 + +### Updating the Service + +When you update the code: + +**Option 1: Using install.ps1 (Recommended)** + +```powershell +# Stop service, rebuild, republish, and restart +.\install-service.ps1 -Action Stop +.\install.ps1 +.\install-service.ps1 -Action Start +``` + +**Option 2: Manual Method** + +```powershell +# 1. Stop the service +.\install-service.ps1 -Action Stop + +# 2. Republish +dotnet publish -c Release -o publish + +# 3. Restart the service +.\install-service.ps1 -Action Start +``` + +**Option 3: Full Reinstall** + +```powershell +# Uninstall, republish, and reinstall +.\install-service.ps1 -Action Uninstall +.\install.ps1 -InstallService -StartService +``` + +## Alternative: Manual Service Installation + +If you prefer to use `sc.exe` directly: + +```cmd +# Install +sc create RoslynBridgeWebApi binPath="C:\Path\To\publish\RoslynBridge.WebApi.exe" start=auto + +# Start +sc start RoslynBridgeWebApi + +# Stop +sc stop RoslynBridgeWebApi + +# Delete +sc delete RoslynBridgeWebApi +``` + +## Running in Development + +For development, you don't need to install as a service. Just run: + +```powershell +dotnet run --urls "http://localhost:5000" +``` + +The application will work the same way but won't persist after closing the terminal. + +## Security Considerations + +### Production Deployment + +For production environments: + +1. **Use HTTPS**: Configure SSL certificates in `appsettings.json` +2. **Restrict CORS**: Update the CORS policy to allow only specific origins +3. **Add Authentication**: Consider adding API key or OAuth authentication +4. **Firewall**: Only allow access from trusted IPs + +### Network Access + +By default, the service only listens on `localhost:5000`. To allow external access: + +```json +{ + "Kestrel": { + "Endpoints": { + "Http": { + "Url": "http://0.0.0.0:5000" + } + } + } +} +``` + +**Warning**: Only do this in trusted networks. Add authentication first! + +## Service Lifecycle + +The service: +1. **Starts automatically** when Windows boots +2. **Requires Visual Studio** to be running (with a solution loaded) for full functionality +3. **Tracks request history** in memory (lost on restart) +4. **Logs to Event Log** for monitoring + +## Monitoring + +### Check Service Status + +```powershell +# PowerShell +Get-Service RoslynBridgeWebApi + +# Or use the script +.\install-service.ps1 -Action Status +``` + +### View Logs + +```powershell +# View recent logs +Get-EventLog -LogName Application -Source "Roslyn Bridge Web API" -Newest 20 + +# Monitor logs in real-time +Get-EventLog -LogName Application -Source "Roslyn Bridge Web API" -Newest 1 -AsString +# Press Ctrl+C to stop +``` + +### Test API Health + +```powershell +# Quick ping +curl http://localhost:5000/api/health/ping + +# Full health check (includes VS plugin status) +curl http://localhost:5000/api/health + +# View Swagger documentation +Start-Process "http://localhost:5000" +``` + +## Uninstalling + +To completely remove the service: + +```powershell +# Stop and uninstall +.\install-service.ps1 -Action Uninstall + +# Optional: Delete the publish folder +Remove-Item -Path ".\publish" -Recurse -Force +``` + +## Additional Resources + +- **Swagger UI**: http://localhost:5000 (when service is running) +- **Health Check**: http://localhost:5000/api/health +- **History Stats**: http://localhost:5000/api/history/stats +- **Event Viewer**: Windows Logs → Application → "Roslyn Bridge Web API" + +## FAQ + +**Q: Do I need to keep Visual Studio open?** +A: Yes, the VS plugin (port 59123) must be running. The Web API is just a middleware layer. + +**Q: Can I run multiple instances?** +A: Yes, but change the port in `appsettings.json` for each instance. + +**Q: What happens if the service can't connect to VS plugin?** +A: API calls will return errors, but the service remains running. History and health endpoints still work. + +**Q: Can I use this without the Web API?** +A: Yes, you can call the VS plugin directly on port 59123 using POST requests (see roslyn-bridge skill). + +**Q: How do I backup request history?** +A: History is in-memory only. Consider implementing database persistence for production use. diff --git a/RoslynBridge.WebApi/Services/HistoryService.cs b/RoslynBridge.WebApi/Services/HistoryService.cs new file mode 100644 index 0000000..03df7b5 --- /dev/null +++ b/RoslynBridge.WebApi/Services/HistoryService.cs @@ -0,0 +1,59 @@ +using System.Collections.Concurrent; +using RoslynBridge.WebApi.Models; + +namespace RoslynBridge.WebApi.Services; + +/// +/// In-memory implementation of history service +/// +public class HistoryService : IHistoryService +{ + private readonly ConcurrentQueue _entries = new(); + private readonly ILogger _logger; + private readonly int _maxEntries; + + public HistoryService(ILogger logger, IConfiguration configuration) + { + _logger = logger; + _maxEntries = configuration.GetValue("History:MaxEntries", 1000); + } + + public void Add(QueryHistoryEntry entry) + { + _entries.Enqueue(entry); + + // Trim old entries if we exceed max + while (_entries.Count > _maxEntries) + { + _entries.TryDequeue(out _); + } + + _logger.LogDebug("Added history entry: {Id} - {Path}", entry.Id, entry.Path); + } + + public IEnumerable GetAll() + { + return _entries.Reverse(); + } + + public QueryHistoryEntry? GetById(string id) + { + return _entries.FirstOrDefault(e => e.Id == id); + } + + public IEnumerable GetRecent(int count = 50) + { + return _entries.Reverse().Take(count); + } + + public void Clear() + { + _entries.Clear(); + _logger.LogInformation("History cleared"); + } + + public int GetCount() + { + return _entries.Count; + } +} diff --git a/RoslynBridge.WebApi/Services/IHistoryService.cs b/RoslynBridge.WebApi/Services/IHistoryService.cs new file mode 100644 index 0000000..7ad29dc --- /dev/null +++ b/RoslynBridge.WebApi/Services/IHistoryService.cs @@ -0,0 +1,46 @@ +using RoslynBridge.WebApi.Models; + +namespace RoslynBridge.WebApi.Services; + +/// +/// Service for managing query history +/// +public interface IHistoryService +{ + /// + /// Add a new history entry + /// + /// The history entry to add + void Add(QueryHistoryEntry entry); + + /// + /// Get all history entries + /// + /// List of all history entries + IEnumerable GetAll(); + + /// + /// Get a specific history entry by ID + /// + /// The entry ID + /// The history entry, or null if not found + QueryHistoryEntry? GetById(string id); + + /// + /// Get recent history entries + /// + /// Number of entries to return + /// List of recent history entries + IEnumerable GetRecent(int count = 50); + + /// + /// Clear all history entries + /// + void Clear(); + + /// + /// Get total count of history entries + /// + /// Total number of entries + int GetCount(); +} diff --git a/RoslynBridge.WebApi/Services/IInstanceRegistryService.cs b/RoslynBridge.WebApi/Services/IInstanceRegistryService.cs new file mode 100644 index 0000000..9e1b218 --- /dev/null +++ b/RoslynBridge.WebApi/Services/IInstanceRegistryService.cs @@ -0,0 +1,49 @@ +using RoslynBridge.WebApi.Models; + +namespace RoslynBridge.WebApi.Services; + +/// +/// Service for managing registered Visual Studio instances +/// +public interface IInstanceRegistryService +{ + /// + /// Register a new Visual Studio instance + /// + void Register(VSInstanceInfo instance); + + /// + /// Unregister a Visual Studio instance by process ID + /// + bool Unregister(int processId); + + /// + /// Update heartbeat for an instance + /// + bool UpdateHeartbeat(int processId); + + /// + /// Get all registered instances + /// + IEnumerable GetAllInstances(); + + /// + /// Get instance by process ID + /// + VSInstanceInfo? GetByProcessId(int processId); + + /// + /// Get instance by solution path + /// + VSInstanceInfo? GetBySolutionPath(string solutionPath); + + /// + /// Get instance by port + /// + VSInstanceInfo? GetByPort(int port); + + /// + /// Remove stale instances (no heartbeat for specified timeout) + /// + void RemoveStaleInstances(TimeSpan timeout); +} diff --git a/RoslynBridge.WebApi/Services/IRoslynBridgeClient.cs b/RoslynBridge.WebApi/Services/IRoslynBridgeClient.cs new file mode 100644 index 0000000..7d95e90 --- /dev/null +++ b/RoslynBridge.WebApi/Services/IRoslynBridgeClient.cs @@ -0,0 +1,26 @@ +using RoslynBridge.WebApi.Models; + +namespace RoslynBridge.WebApi.Services; + +/// +/// Interface for communicating with the Roslyn Bridge Visual Studio plugin +/// +public interface IRoslynBridgeClient +{ + /// + /// Execute a query against the Roslyn Bridge server + /// + /// The query request + /// Optional port of specific VS instance to target + /// Cancellation token + /// The query response + Task ExecuteQueryAsync(RoslynQueryRequest request, int? instancePort = null, CancellationToken cancellationToken = default); + + /// + /// Check if the Roslyn Bridge server is healthy + /// + /// Optional port of specific VS instance to check + /// Cancellation token + /// True if healthy, false otherwise + Task IsHealthyAsync(int? instancePort = null, CancellationToken cancellationToken = default); +} diff --git a/RoslynBridge.WebApi/Services/InstanceCleanupService.cs b/RoslynBridge.WebApi/Services/InstanceCleanupService.cs new file mode 100644 index 0000000..9694388 --- /dev/null +++ b/RoslynBridge.WebApi/Services/InstanceCleanupService.cs @@ -0,0 +1,50 @@ +namespace RoslynBridge.WebApi.Services; + +/// +/// Background service that periodically removes stale VS instances +/// +public class InstanceCleanupService : BackgroundService +{ + private readonly IServiceProvider _serviceProvider; + private readonly ILogger _logger; + private readonly TimeSpan _cleanupInterval = TimeSpan.FromMinutes(1); + private readonly TimeSpan _staleTimeout = TimeSpan.FromMinutes(5); + + public InstanceCleanupService( + IServiceProvider serviceProvider, + ILogger logger) + { + _serviceProvider = serviceProvider; + _logger = logger; + } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + _logger.LogInformation("Instance cleanup service started"); + + while (!stoppingToken.IsCancellationRequested) + { + try + { + await Task.Delay(_cleanupInterval, stoppingToken); + + // Get the registry service from scope + using var scope = _serviceProvider.CreateScope(); + var registryService = scope.ServiceProvider.GetRequiredService(); + + registryService.RemoveStaleInstances(_staleTimeout); + } + catch (OperationCanceledException) + { + // Expected when stopping + break; + } + catch (Exception ex) + { + _logger.LogError(ex, "Error during instance cleanup"); + } + } + + _logger.LogInformation("Instance cleanup service stopped"); + } +} diff --git a/RoslynBridge.WebApi/Services/InstanceRegistryService.cs b/RoslynBridge.WebApi/Services/InstanceRegistryService.cs new file mode 100644 index 0000000..7fd58f1 --- /dev/null +++ b/RoslynBridge.WebApi/Services/InstanceRegistryService.cs @@ -0,0 +1,115 @@ +using System.Collections.Concurrent; +using RoslynBridge.WebApi.Models; + +namespace RoslynBridge.WebApi.Services; + +/// +/// Thread-safe in-memory registry for Visual Studio instances +/// +public class InstanceRegistryService : IInstanceRegistryService +{ + private readonly ConcurrentDictionary _instances = new(); + private readonly ILogger _logger; + + public InstanceRegistryService(ILogger logger) + { + _logger = logger; + } + + public void Register(VSInstanceInfo instance) + { + instance.RegisteredAt = DateTime.UtcNow; + instance.LastHeartbeat = DateTime.UtcNow; + + _instances.AddOrUpdate(instance.ProcessId, instance, (_, existing) => + { + // Update existing instance + existing.Port = instance.Port; + existing.SolutionPath = instance.SolutionPath; + existing.SolutionName = instance.SolutionName; + existing.Projects = instance.Projects; + existing.LastHeartbeat = DateTime.UtcNow; + return existing; + }); + + _logger.LogInformation( + "Registered VS instance: PID={ProcessId}, Port={Port}, Solution={Solution}", + instance.ProcessId, + instance.Port, + instance.SolutionName ?? "None"); + } + + public bool Unregister(int processId) + { + var removed = _instances.TryRemove(processId, out var instance); + + if (removed) + { + _logger.LogInformation( + "Unregistered VS instance: PID={ProcessId}, Solution={Solution}", + processId, + instance?.SolutionName ?? "None"); + } + + return removed; + } + + public bool UpdateHeartbeat(int processId) + { + if (_instances.TryGetValue(processId, out var instance)) + { + instance.LastHeartbeat = DateTime.UtcNow; + _logger.LogDebug("Updated heartbeat for VS instance: PID={ProcessId}", processId); + return true; + } + + return false; + } + + public IEnumerable GetAllInstances() + { + return _instances.Values.ToList(); + } + + public VSInstanceInfo? GetByProcessId(int processId) + { + _instances.TryGetValue(processId, out var instance); + return instance; + } + + public VSInstanceInfo? GetBySolutionPath(string solutionPath) + { + if (string.IsNullOrEmpty(solutionPath)) + return null; + + var normalizedPath = Path.GetFullPath(solutionPath).ToLowerInvariant(); + + return _instances.Values.FirstOrDefault(i => + !string.IsNullOrEmpty(i.SolutionPath) && + Path.GetFullPath(i.SolutionPath).ToLowerInvariant() == normalizedPath); + } + + public VSInstanceInfo? GetByPort(int port) + { + return _instances.Values.FirstOrDefault(i => i.Port == port); + } + + public void RemoveStaleInstances(TimeSpan timeout) + { + var cutoff = DateTime.UtcNow - timeout; + var staleInstances = _instances.Values + .Where(i => i.LastHeartbeat < cutoff) + .ToList(); + + foreach (var instance in staleInstances) + { + if (_instances.TryRemove(instance.ProcessId, out _)) + { + _logger.LogWarning( + "Removed stale VS instance: PID={ProcessId}, LastHeartbeat={LastHeartbeat}", + instance.ProcessId, + instance.LastHeartbeat); + } + } + } +} diff --git a/RoslynBridge.WebApi/Services/RoslynBridgeClient.cs b/RoslynBridge.WebApi/Services/RoslynBridgeClient.cs new file mode 100644 index 0000000..7f59199 --- /dev/null +++ b/RoslynBridge.WebApi/Services/RoslynBridgeClient.cs @@ -0,0 +1,174 @@ +using System.Text; +using System.Text.Json; +using RoslynBridge.WebApi.Models; + +namespace RoslynBridge.WebApi.Services; + +/// +/// HTTP client for communicating with the Roslyn Bridge Visual Studio plugin +/// +public class RoslynBridgeClient : IRoslynBridgeClient +{ + private readonly HttpClient _httpClient; + private readonly IInstanceRegistryService _registryService; + private readonly ILogger _logger; + private readonly JsonSerializerOptions _jsonOptions; + + public RoslynBridgeClient( + HttpClient httpClient, + IInstanceRegistryService registryService, + ILogger logger) + { + _httpClient = httpClient; + _registryService = registryService; + _logger = logger; + _jsonOptions = new JsonSerializerOptions + { + PropertyNameCaseInsensitive = true, + PropertyNamingPolicy = JsonNamingPolicy.CamelCase + }; + } + + public async Task ExecuteQueryAsync( + RoslynQueryRequest request, + int? instancePort = null, + CancellationToken cancellationToken = default) + { + try + { + var targetPort = await ResolveInstancePortAsync(instancePort, request); + + if (targetPort == null) + { + return new RoslynQueryResponse + { + Success = false, + Error = "No Visual Studio instance available" + }; + } + + _logger.LogInformation("Executing query: {QueryType} on port {Port}", request.QueryType, targetPort); + + var json = JsonSerializer.Serialize(request, _jsonOptions); + var content = new StringContent(json, Encoding.UTF8, "application/json"); + + var url = $"http://localhost:{targetPort}/query"; + var response = await _httpClient.PostAsync(url, content, cancellationToken); + + if (!response.IsSuccessStatusCode) + { + var errorContent = await response.Content.ReadAsStringAsync(cancellationToken); + _logger.LogError("Query failed with status {StatusCode}: {Error}", response.StatusCode, errorContent); + + return new RoslynQueryResponse + { + Success = false, + Error = $"Request failed with status {response.StatusCode}: {errorContent}" + }; + } + + var responseContent = await response.Content.ReadAsStringAsync(cancellationToken); + var result = JsonSerializer.Deserialize(responseContent, _jsonOptions); + + if (result == null) + { + return new RoslynQueryResponse + { + Success = false, + Error = "Failed to deserialize response" + }; + } + + _logger.LogInformation("Query executed successfully: {Success}", result.Success); + return result; + } + catch (HttpRequestException ex) + { + _logger.LogError(ex, "HTTP request failed while executing query"); + return new RoslynQueryResponse + { + Success = false, + Error = $"Failed to connect to Roslyn Bridge server: {ex.Message}" + }; + } + catch (Exception ex) + { + _logger.LogError(ex, "Unexpected error while executing query"); + return new RoslynQueryResponse + { + Success = false, + Error = $"Unexpected error: {ex.Message}" + }; + } + } + + public async Task IsHealthyAsync(int? instancePort = null, CancellationToken cancellationToken = default) + { + try + { + var targetPort = await ResolveInstancePortAsync(instancePort, null); + + if (targetPort == null) + { + return false; + } + + var request = new RoslynQueryRequest { QueryType = "health" }; + var json = JsonSerializer.Serialize(request, _jsonOptions); + var content = new StringContent(json, Encoding.UTF8, "application/json"); + + var url = $"http://localhost:{targetPort}/health"; + var response = await _httpClient.PostAsync(url, content, cancellationToken); + return response.IsSuccessStatusCode; + } + catch (Exception ex) + { + _logger.LogWarning(ex, "Health check failed"); + return false; + } + } + + /// + /// Resolves which VS instance port to use based on provided hints + /// + private Task ResolveInstancePortAsync(int? explicitPort, RoslynQueryRequest? request) + { + // If explicit port specified, use it + if (explicitPort.HasValue) + { + return Task.FromResult(explicitPort.Value); + } + + // Try to find instance by solution path from request + if (request != null && !string.IsNullOrEmpty(request.FilePath)) + { + // Extract solution path by looking for .sln file in the path hierarchy + var directory = Path.GetDirectoryName(request.FilePath); + while (!string.IsNullOrEmpty(directory)) + { + var solutionFiles = Directory.GetFiles(directory, "*.sln"); + if (solutionFiles.Length > 0) + { + var instance = _registryService.GetBySolutionPath(solutionFiles[0]); + if (instance != null) + { + _logger.LogDebug("Found instance by solution path: {SolutionPath}", solutionFiles[0]); + return Task.FromResult(instance.Port); + } + } + directory = Path.GetDirectoryName(directory); + } + } + + // Fall back to first available instance + var instances = _registryService.GetAllInstances().ToList(); + if (instances.Any()) + { + _logger.LogDebug("Using first available instance: port {Port}", instances[0].Port); + return Task.FromResult(instances[0].Port); + } + + _logger.LogWarning("No Visual Studio instances registered"); + return Task.FromResult(null); + } +} diff --git a/RoslynBridge.WebApi/appsettings.Development.json b/RoslynBridge.WebApi/appsettings.Development.json new file mode 100644 index 0000000..d9e464b --- /dev/null +++ b/RoslynBridge.WebApi/appsettings.Development.json @@ -0,0 +1,12 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning", + "RoslynBridge.WebApi": "Debug" + } + }, + "RoslynBridge": { + "BaseUrl": "http://localhost:59123" + } +} diff --git a/RoslynBridge.WebApi/appsettings.json b/RoslynBridge.WebApi/appsettings.json new file mode 100644 index 0000000..bc6705a --- /dev/null +++ b/RoslynBridge.WebApi/appsettings.json @@ -0,0 +1,33 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning", + "RoslynBridge.WebApi": "Information" + }, + "EventLog": { + "SourceName": "Roslyn Bridge Web API", + "LogName": "Application", + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } + }, + "AllowedHosts": "*", + "RoslynBridge": { + "BaseUrl": "http://localhost:59123", + "TimeoutSeconds": 30 + }, + "History": { + "MaxEntries": 1000, + "Enabled": true + }, + "Kestrel": { + "Endpoints": { + "Http": { + "Url": "http://localhost:5001" + } + } + } +} diff --git a/RoslynBridge.WebApi/install.ps1 b/RoslynBridge.WebApi/install.ps1 new file mode 100644 index 0000000..a1092e3 --- /dev/null +++ b/RoslynBridge.WebApi/install.ps1 @@ -0,0 +1,361 @@ +#Requires -RunAsAdministrator + +<# +.SYNOPSIS + Complete installation script for Roslyn Bridge Web API + +.DESCRIPTION + This script performs a complete installation of the Roslyn Bridge Web API: + - Checks prerequisites (.NET SDK) + - Restores NuGet packages + - Builds the project + - Publishes the release build + - Optionally installs as Windows Service + - Tests the installation + +.PARAMETER SkipBuild + Skip the build step (use existing build) + +.PARAMETER SkipPublish + Skip the publish step (use existing publish) + +.PARAMETER InstallService + Install as Windows Service after publishing + +.PARAMETER StartService + Start the service after installation (requires -InstallService) + +.PARAMETER Configuration + Build configuration (Debug or Release). Default: Release + +.PARAMETER PublishPath + Path where the application will be published. Default: ./publish + +.EXAMPLE + .\install.ps1 + Full installation without service setup + +.EXAMPLE + .\install.ps1 -InstallService -StartService + Full installation with automatic service setup and start + +.EXAMPLE + .\install.ps1 -Configuration Debug + Install debug build instead of release +#> + +param( + [Parameter(Mandatory=$false)] + [switch]$SkipBuild, + + [Parameter(Mandatory=$false)] + [switch]$SkipPublish, + + [Parameter(Mandatory=$false)] + [switch]$InstallService, + + [Parameter(Mandatory=$false)] + [switch]$StartService, + + [Parameter(Mandatory=$false)] + [ValidateSet("Debug", "Release")] + [string]$Configuration = "Release", + + [Parameter(Mandatory=$false)] + [string]$PublishPath = ".\publish" +) + +# Script configuration +$ErrorActionPreference = "Stop" +$ProjectName = "RoslynBridge.WebApi" +$ProjectFile = ".\RoslynBridge.WebApi.csproj" + +# Color output helper +function Write-ColorOutput { + param( + [string]$Message, + [string]$Color = "White" + ) + Write-Host $Message -ForegroundColor $Color +} + +# Banner +function Show-Banner { + Write-ColorOutput "`n============================================================" "Cyan" + Write-ColorOutput " Roslyn Bridge Web API - Installation Script" "Cyan" + Write-ColorOutput "============================================================`n" "Cyan" +} + +# Step counter +$script:stepNumber = 0 +function Write-Step { + param([string]$Message) + $script:stepNumber++ + Write-ColorOutput "`n[$script:stepNumber] $Message" "Yellow" + Write-ColorOutput ("-" * 60) "DarkGray" +} + +# Check prerequisites +function Test-Prerequisites { + Write-Step "Checking Prerequisites" + + # Check for .NET SDK + Write-Host "Checking for .NET SDK... " -NoNewline + try { + $dotnetVersion = dotnet --version 2>$null + if ($LASTEXITCODE -eq 0) { + Write-ColorOutput "Found v$dotnetVersion" "Green" + } else { + throw "dotnet command failed" + } + } + catch { + Write-ColorOutput "NOT FOUND" "Red" + Write-ColorOutput "`nError: .NET SDK is not installed or not in PATH." "Red" + Write-ColorOutput "Please download and install from: https://dot.net" "Yellow" + exit 1 + } + + # Check for project file + Write-Host "Checking for project file... " -NoNewline + if (Test-Path $ProjectFile) { + Write-ColorOutput "Found" "Green" + } else { + Write-ColorOutput "NOT FOUND" "Red" + Write-ColorOutput "`nError: Project file not found: $ProjectFile" "Red" + Write-ColorOutput "Please run this script from the RoslynBridge.WebApi directory." "Yellow" + exit 1 + } + + Write-ColorOutput "`nAll prerequisites satisfied!" "Green" +} + +# Restore NuGet packages +function Restore-Packages { + Write-Step "Restoring NuGet Packages" + + try { + dotnet restore $ProjectFile + if ($LASTEXITCODE -ne 0) { + throw "dotnet restore failed with exit code $LASTEXITCODE" + } + Write-ColorOutput "`nPackages restored successfully!" "Green" + } + catch { + Write-ColorOutput "`nError during package restore: $_" "Red" + exit 1 + } +} + +# Build project +function Build-Project { + Write-Step "Building Project ($Configuration)" + + try { + dotnet build $ProjectFile -c $Configuration --no-restore + if ($LASTEXITCODE -ne 0) { + throw "dotnet build failed with exit code $LASTEXITCODE" + } + Write-ColorOutput "`nBuild completed successfully!" "Green" + } + catch { + Write-ColorOutput "`nError during build: $_" "Red" + exit 1 + } +} + +# Publish project +function Publish-Project { + Write-Step "Publishing Project" + + Write-ColorOutput "Configuration: $Configuration" "Gray" + Write-ColorOutput "Output Path: $PublishPath" "Gray" + + try { + # Clean publish directory if it exists + if (Test-Path $PublishPath) { + Write-Host "`nCleaning existing publish directory... " -NoNewline + Remove-Item -Path $PublishPath -Recurse -Force + Write-ColorOutput "Done" "Green" + } + + # Publish + dotnet publish $ProjectFile -c $Configuration -o $PublishPath --no-build --no-restore + if ($LASTEXITCODE -ne 0) { + throw "dotnet publish failed with exit code $LASTEXITCODE" + } + + # Verify executable exists + $exePath = Join-Path $PublishPath "$ProjectName.exe" + if (Test-Path $exePath) { + Write-ColorOutput "`nProject published successfully!" "Green" + Write-ColorOutput "Executable: $exePath" "Gray" + + # Show publish directory size + $publishSize = (Get-ChildItem $PublishPath -Recurse | Measure-Object -Property Length -Sum).Sum / 1MB + Write-ColorOutput ("Publish size: {0:N2} MB" -f $publishSize) "Gray" + } else { + throw "Executable not found after publish: $exePath" + } + } + catch { + Write-ColorOutput "`nError during publish: $_" "Red" + exit 1 + } +} + +# Install Windows Service +function Install-WindowsService { + Write-Step "Installing Windows Service" + + if (-not (Test-Path ".\install-service.ps1")) { + Write-ColorOutput "Error: install-service.ps1 not found" "Red" + return $false + } + + try { + & ".\install-service.ps1" -Action Install -PublishPath $PublishPath + return $true + } + catch { + Write-ColorOutput "Error installing service: $_" "Red" + return $false + } +} + +# Start Windows Service +function Start-WindowsService { + Write-Step "Starting Windows Service" + + if (-not (Test-Path ".\install-service.ps1")) { + Write-ColorOutput "Error: install-service.ps1 not found" "Red" + return $false + } + + try { + & ".\install-service.ps1" -Action Start + return $true + } + catch { + Write-ColorOutput "Error starting service: $_" "Red" + return $false + } +} + +# Test installation +function Test-Installation { + Write-Step "Installation Summary" + + $publishFullPath = Resolve-Path $PublishPath -ErrorAction SilentlyContinue + if ($publishFullPath) { + Write-ColorOutput "`nPublished to: $publishFullPath" "Green" + + $exePath = Join-Path $publishFullPath "$ProjectName.exe" + if (Test-Path $exePath) { + Write-ColorOutput "Executable: $exePath" "Green" + } + } + + if ($InstallService) { + $service = Get-Service -Name "RoslynBridgeWebApi" -ErrorAction SilentlyContinue + if ($service) { + Write-ColorOutput "`nWindows Service Status:" "Cyan" + Write-ColorOutput " Name: $($service.Name)" "Gray" + Write-ColorOutput " Status: $($service.Status)" $(if ($service.Status -eq "Running") { "Green" } else { "Yellow" }) + Write-ColorOutput " Type: $($service.StartType)" "Gray" + } + } +} + +# Show next steps +function Show-NextSteps { + Write-Step "Next Steps" + + if ($InstallService) { + if ($StartService) { + Write-ColorOutput "`nThe service is now running!" "Green" + Write-ColorOutput "`nAPI should be available at:" "Cyan" + Write-ColorOutput " http://localhost:5000" "White" + Write-ColorOutput " https://localhost:7001 (HTTPS)" "White" + Write-ColorOutput "`nSwagger UI:" "Cyan" + Write-ColorOutput " http://localhost:5000" "White" + Write-ColorOutput "`nTo manage the service:" "Yellow" + Write-ColorOutput " .\install-service.ps1 -Action Status" "White" + Write-ColorOutput " .\install-service.ps1 -Action Stop" "White" + Write-ColorOutput " .\install-service.ps1 -Action Restart" "White" + Write-ColorOutput " .\install-service.ps1 -Action Uninstall" "White" + } else { + Write-ColorOutput "`nService installed but not started." "Yellow" + Write-ColorOutput "`nTo start the service:" "Cyan" + Write-ColorOutput " .\install-service.ps1 -Action Start" "White" + } + } else { + Write-ColorOutput "`nTo run the application manually:" "Cyan" + Write-ColorOutput " cd $PublishPath" "White" + Write-ColorOutput " .\$ProjectName.exe" "White" + Write-ColorOutput "`nTo install as a Windows Service:" "Cyan" + Write-ColorOutput " .\install-service.ps1 -Action Install -PublishPath $PublishPath" "White" + Write-ColorOutput " .\install-service.ps1 -Action Start" "White" + Write-ColorOutput "`nOr run this script again with -InstallService -StartService flags" "Gray" + } + + Write-ColorOutput "`nFor more information, see README.md" "Gray" +} + +# Main execution +try { + Show-Banner + + # Show configuration + Write-ColorOutput "Installation Configuration:" "Cyan" + Write-ColorOutput " Configuration: $Configuration" "Gray" + Write-ColorOutput " Publish Path: $PublishPath" "Gray" + Write-ColorOutput " Install Service: $InstallService" "Gray" + Write-ColorOutput " Start Service: $StartService" "Gray" + Write-ColorOutput " Skip Build: $SkipBuild" "Gray" + Write-ColorOutput " Skip Publish: $SkipPublish" "Gray" + + # Execute installation steps + Test-Prerequisites + + if (-not $SkipBuild) { + Restore-Packages + Build-Project + } else { + Write-ColorOutput "`nSkipping build (using existing build)" "Yellow" + } + + if (-not $SkipPublish) { + Publish-Project + } else { + Write-ColorOutput "`nSkipping publish (using existing publish)" "Yellow" + } + + # Optional service installation + if ($InstallService) { + $serviceInstalled = Install-WindowsService + + if ($serviceInstalled -and $StartService) { + Start-WindowsService + } + } + + # Show results + Test-Installation + Show-NextSteps + + Write-ColorOutput "`n============================================================" "Cyan" + Write-ColorOutput " Installation Completed Successfully!" "Cyan" + Write-ColorOutput "============================================================`n" "Cyan" + + exit 0 +} +catch { + Write-ColorOutput "`n============================================================" "Red" + Write-ColorOutput " Installation Failed!" "Red" + Write-ColorOutput "============================================================`n" "Red" + Write-ColorOutput "Error: $_" "Red" + Write-ColorOutput "`nStack Trace:" "DarkGray" + Write-ColorOutput $_.ScriptStackTrace "DarkGray" + exit 1 +} diff --git a/RoslynBridge.WebApi/publish/Microsoft.Extensions.Configuration.Abstractions.dll b/RoslynBridge.WebApi/publish/Microsoft.Extensions.Configuration.Abstractions.dll new file mode 100644 index 0000000..58f5b7d Binary files /dev/null and b/RoslynBridge.WebApi/publish/Microsoft.Extensions.Configuration.Abstractions.dll differ diff --git a/RoslynBridge.WebApi/publish/Microsoft.Extensions.Configuration.Binder.dll b/RoslynBridge.WebApi/publish/Microsoft.Extensions.Configuration.Binder.dll new file mode 100644 index 0000000..a95f048 Binary files /dev/null and b/RoslynBridge.WebApi/publish/Microsoft.Extensions.Configuration.Binder.dll differ diff --git a/RoslynBridge.WebApi/publish/Microsoft.Extensions.Configuration.CommandLine.dll b/RoslynBridge.WebApi/publish/Microsoft.Extensions.Configuration.CommandLine.dll new file mode 100644 index 0000000..717282c Binary files /dev/null and b/RoslynBridge.WebApi/publish/Microsoft.Extensions.Configuration.CommandLine.dll differ diff --git a/RoslynBridge.WebApi/publish/Microsoft.Extensions.Configuration.EnvironmentVariables.dll b/RoslynBridge.WebApi/publish/Microsoft.Extensions.Configuration.EnvironmentVariables.dll new file mode 100644 index 0000000..0eda807 Binary files /dev/null and b/RoslynBridge.WebApi/publish/Microsoft.Extensions.Configuration.EnvironmentVariables.dll differ diff --git a/RoslynBridge.WebApi/publish/Microsoft.Extensions.Configuration.FileExtensions.dll b/RoslynBridge.WebApi/publish/Microsoft.Extensions.Configuration.FileExtensions.dll new file mode 100644 index 0000000..e1f5f1d Binary files /dev/null and b/RoslynBridge.WebApi/publish/Microsoft.Extensions.Configuration.FileExtensions.dll differ diff --git a/RoslynBridge.WebApi/publish/Microsoft.Extensions.Configuration.Json.dll b/RoslynBridge.WebApi/publish/Microsoft.Extensions.Configuration.Json.dll new file mode 100644 index 0000000..e22c215 Binary files /dev/null and b/RoslynBridge.WebApi/publish/Microsoft.Extensions.Configuration.Json.dll differ diff --git a/RoslynBridge.WebApi/publish/Microsoft.Extensions.Configuration.UserSecrets.dll b/RoslynBridge.WebApi/publish/Microsoft.Extensions.Configuration.UserSecrets.dll new file mode 100644 index 0000000..f3d8bd4 Binary files /dev/null and b/RoslynBridge.WebApi/publish/Microsoft.Extensions.Configuration.UserSecrets.dll differ diff --git a/RoslynBridge.WebApi/publish/Microsoft.Extensions.Configuration.dll b/RoslynBridge.WebApi/publish/Microsoft.Extensions.Configuration.dll new file mode 100644 index 0000000..155d6f0 Binary files /dev/null and b/RoslynBridge.WebApi/publish/Microsoft.Extensions.Configuration.dll differ diff --git a/RoslynBridge.WebApi/publish/Microsoft.Extensions.DependencyInjection.Abstractions.dll b/RoslynBridge.WebApi/publish/Microsoft.Extensions.DependencyInjection.Abstractions.dll new file mode 100644 index 0000000..a01968f Binary files /dev/null and b/RoslynBridge.WebApi/publish/Microsoft.Extensions.DependencyInjection.Abstractions.dll differ diff --git a/RoslynBridge.WebApi/publish/Microsoft.Extensions.DependencyInjection.dll b/RoslynBridge.WebApi/publish/Microsoft.Extensions.DependencyInjection.dll new file mode 100644 index 0000000..7420234 Binary files /dev/null and b/RoslynBridge.WebApi/publish/Microsoft.Extensions.DependencyInjection.dll differ diff --git a/RoslynBridge.WebApi/publish/Microsoft.Extensions.Diagnostics.Abstractions.dll b/RoslynBridge.WebApi/publish/Microsoft.Extensions.Diagnostics.Abstractions.dll new file mode 100644 index 0000000..7b83ce0 Binary files /dev/null and b/RoslynBridge.WebApi/publish/Microsoft.Extensions.Diagnostics.Abstractions.dll differ diff --git a/RoslynBridge.WebApi/publish/Microsoft.Extensions.Diagnostics.dll b/RoslynBridge.WebApi/publish/Microsoft.Extensions.Diagnostics.dll new file mode 100644 index 0000000..8738391 Binary files /dev/null and b/RoslynBridge.WebApi/publish/Microsoft.Extensions.Diagnostics.dll differ diff --git a/RoslynBridge.WebApi/publish/Microsoft.Extensions.FileProviders.Abstractions.dll b/RoslynBridge.WebApi/publish/Microsoft.Extensions.FileProviders.Abstractions.dll new file mode 100644 index 0000000..af76139 Binary files /dev/null and b/RoslynBridge.WebApi/publish/Microsoft.Extensions.FileProviders.Abstractions.dll differ diff --git a/RoslynBridge.WebApi/publish/Microsoft.Extensions.FileProviders.Physical.dll b/RoslynBridge.WebApi/publish/Microsoft.Extensions.FileProviders.Physical.dll new file mode 100644 index 0000000..270f86b Binary files /dev/null and b/RoslynBridge.WebApi/publish/Microsoft.Extensions.FileProviders.Physical.dll differ diff --git a/RoslynBridge.WebApi/publish/Microsoft.Extensions.FileSystemGlobbing.dll b/RoslynBridge.WebApi/publish/Microsoft.Extensions.FileSystemGlobbing.dll new file mode 100644 index 0000000..5625b24 Binary files /dev/null and b/RoslynBridge.WebApi/publish/Microsoft.Extensions.FileSystemGlobbing.dll differ diff --git a/RoslynBridge.WebApi/publish/Microsoft.Extensions.Hosting.Abstractions.dll b/RoslynBridge.WebApi/publish/Microsoft.Extensions.Hosting.Abstractions.dll new file mode 100644 index 0000000..f979769 Binary files /dev/null and b/RoslynBridge.WebApi/publish/Microsoft.Extensions.Hosting.Abstractions.dll differ diff --git a/RoslynBridge.WebApi/publish/Microsoft.Extensions.Hosting.WindowsServices.dll b/RoslynBridge.WebApi/publish/Microsoft.Extensions.Hosting.WindowsServices.dll new file mode 100644 index 0000000..0ad91c0 Binary files /dev/null and b/RoslynBridge.WebApi/publish/Microsoft.Extensions.Hosting.WindowsServices.dll differ diff --git a/RoslynBridge.WebApi/publish/Microsoft.Extensions.Hosting.dll b/RoslynBridge.WebApi/publish/Microsoft.Extensions.Hosting.dll new file mode 100644 index 0000000..e5fee25 Binary files /dev/null and b/RoslynBridge.WebApi/publish/Microsoft.Extensions.Hosting.dll differ diff --git a/RoslynBridge.WebApi/publish/Microsoft.Extensions.Logging.Abstractions.dll b/RoslynBridge.WebApi/publish/Microsoft.Extensions.Logging.Abstractions.dll new file mode 100644 index 0000000..2bd9784 Binary files /dev/null and b/RoslynBridge.WebApi/publish/Microsoft.Extensions.Logging.Abstractions.dll differ diff --git a/RoslynBridge.WebApi/publish/Microsoft.Extensions.Logging.Configuration.dll b/RoslynBridge.WebApi/publish/Microsoft.Extensions.Logging.Configuration.dll new file mode 100644 index 0000000..cee18fb Binary files /dev/null and b/RoslynBridge.WebApi/publish/Microsoft.Extensions.Logging.Configuration.dll differ diff --git a/RoslynBridge.WebApi/publish/Microsoft.Extensions.Logging.Console.dll b/RoslynBridge.WebApi/publish/Microsoft.Extensions.Logging.Console.dll new file mode 100644 index 0000000..1f4da9c Binary files /dev/null and b/RoslynBridge.WebApi/publish/Microsoft.Extensions.Logging.Console.dll differ diff --git a/RoslynBridge.WebApi/publish/Microsoft.Extensions.Logging.Debug.dll b/RoslynBridge.WebApi/publish/Microsoft.Extensions.Logging.Debug.dll new file mode 100644 index 0000000..4423ccd Binary files /dev/null and b/RoslynBridge.WebApi/publish/Microsoft.Extensions.Logging.Debug.dll differ diff --git a/RoslynBridge.WebApi/publish/Microsoft.Extensions.Logging.EventLog.dll b/RoslynBridge.WebApi/publish/Microsoft.Extensions.Logging.EventLog.dll new file mode 100644 index 0000000..c4cc863 Binary files /dev/null and b/RoslynBridge.WebApi/publish/Microsoft.Extensions.Logging.EventLog.dll differ diff --git a/RoslynBridge.WebApi/publish/Microsoft.Extensions.Logging.EventSource.dll b/RoslynBridge.WebApi/publish/Microsoft.Extensions.Logging.EventSource.dll new file mode 100644 index 0000000..8428eb8 Binary files /dev/null and b/RoslynBridge.WebApi/publish/Microsoft.Extensions.Logging.EventSource.dll differ diff --git a/RoslynBridge.WebApi/publish/Microsoft.Extensions.Logging.dll b/RoslynBridge.WebApi/publish/Microsoft.Extensions.Logging.dll new file mode 100644 index 0000000..7819a6c Binary files /dev/null and b/RoslynBridge.WebApi/publish/Microsoft.Extensions.Logging.dll differ diff --git a/RoslynBridge.WebApi/publish/Microsoft.Extensions.Options.ConfigurationExtensions.dll b/RoslynBridge.WebApi/publish/Microsoft.Extensions.Options.ConfigurationExtensions.dll new file mode 100644 index 0000000..30d51ea Binary files /dev/null and b/RoslynBridge.WebApi/publish/Microsoft.Extensions.Options.ConfigurationExtensions.dll differ diff --git a/RoslynBridge.WebApi/publish/Microsoft.Extensions.Options.dll b/RoslynBridge.WebApi/publish/Microsoft.Extensions.Options.dll new file mode 100644 index 0000000..6b54341 Binary files /dev/null and b/RoslynBridge.WebApi/publish/Microsoft.Extensions.Options.dll differ diff --git a/RoslynBridge.WebApi/publish/Microsoft.Extensions.Primitives.dll b/RoslynBridge.WebApi/publish/Microsoft.Extensions.Primitives.dll new file mode 100644 index 0000000..5a58056 Binary files /dev/null and b/RoslynBridge.WebApi/publish/Microsoft.Extensions.Primitives.dll differ diff --git a/RoslynBridge.WebApi/publish/Microsoft.OpenApi.dll b/RoslynBridge.WebApi/publish/Microsoft.OpenApi.dll new file mode 100644 index 0000000..aac9a6d Binary files /dev/null and b/RoslynBridge.WebApi/publish/Microsoft.OpenApi.dll differ diff --git a/RoslynBridge.WebApi/publish/RoslynBridge.WebApi.deps.json b/RoslynBridge.WebApi/publish/RoslynBridge.WebApi.deps.json new file mode 100644 index 0000000..688981b --- /dev/null +++ b/RoslynBridge.WebApi/publish/RoslynBridge.WebApi.deps.json @@ -0,0 +1,834 @@ +{ + "runtimeTarget": { + "name": ".NETCoreApp,Version=v8.0", + "signature": "" + }, + "compilationOptions": {}, + "targets": { + ".NETCoreApp,Version=v8.0": { + "RoslynBridge.WebApi/1.0.0": { + "dependencies": { + "Microsoft.Extensions.Hosting.WindowsServices": "9.0.10", + "Microsoft.Extensions.Http": "8.0.0", + "Swashbuckle.AspNetCore": "6.6.2" + }, + "runtime": { + "RoslynBridge.WebApi.dll": {} + } + }, + "Microsoft.Extensions.ApiDescription.Server/6.0.5": {}, + "Microsoft.Extensions.Configuration/9.0.10": { + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "9.0.10", + "Microsoft.Extensions.Primitives": "9.0.10" + }, + "runtime": { + "lib/net8.0/Microsoft.Extensions.Configuration.dll": { + "assemblyVersion": "9.0.0.0", + "fileVersion": "9.0.1025.47515" + } + } + }, + "Microsoft.Extensions.Configuration.Abstractions/9.0.10": { + "dependencies": { + "Microsoft.Extensions.Primitives": "9.0.10" + }, + "runtime": { + "lib/net8.0/Microsoft.Extensions.Configuration.Abstractions.dll": { + "assemblyVersion": "9.0.0.0", + "fileVersion": "9.0.1025.47515" + } + } + }, + "Microsoft.Extensions.Configuration.Binder/9.0.10": { + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "9.0.10" + }, + "runtime": { + "lib/net8.0/Microsoft.Extensions.Configuration.Binder.dll": { + "assemblyVersion": "9.0.0.0", + "fileVersion": "9.0.1025.47515" + } + } + }, + "Microsoft.Extensions.Configuration.CommandLine/9.0.10": { + "dependencies": { + "Microsoft.Extensions.Configuration": "9.0.10", + "Microsoft.Extensions.Configuration.Abstractions": "9.0.10" + }, + "runtime": { + "lib/net8.0/Microsoft.Extensions.Configuration.CommandLine.dll": { + "assemblyVersion": "9.0.0.0", + "fileVersion": "9.0.1025.47515" + } + } + }, + "Microsoft.Extensions.Configuration.EnvironmentVariables/9.0.10": { + "dependencies": { + "Microsoft.Extensions.Configuration": "9.0.10", + "Microsoft.Extensions.Configuration.Abstractions": "9.0.10" + }, + "runtime": { + "lib/net8.0/Microsoft.Extensions.Configuration.EnvironmentVariables.dll": { + "assemblyVersion": "9.0.0.0", + "fileVersion": "9.0.1025.47515" + } + } + }, + "Microsoft.Extensions.Configuration.FileExtensions/9.0.10": { + "dependencies": { + "Microsoft.Extensions.Configuration": "9.0.10", + "Microsoft.Extensions.Configuration.Abstractions": "9.0.10", + "Microsoft.Extensions.FileProviders.Abstractions": "9.0.10", + "Microsoft.Extensions.FileProviders.Physical": "9.0.10", + "Microsoft.Extensions.Primitives": "9.0.10" + }, + "runtime": { + "lib/net8.0/Microsoft.Extensions.Configuration.FileExtensions.dll": { + "assemblyVersion": "9.0.0.0", + "fileVersion": "9.0.1025.47515" + } + } + }, + "Microsoft.Extensions.Configuration.Json/9.0.10": { + "dependencies": { + "Microsoft.Extensions.Configuration": "9.0.10", + "Microsoft.Extensions.Configuration.Abstractions": "9.0.10", + "Microsoft.Extensions.Configuration.FileExtensions": "9.0.10", + "Microsoft.Extensions.FileProviders.Abstractions": "9.0.10", + "System.Text.Json": "9.0.10" + }, + "runtime": { + "lib/net8.0/Microsoft.Extensions.Configuration.Json.dll": { + "assemblyVersion": "9.0.0.0", + "fileVersion": "9.0.1025.47515" + } + } + }, + "Microsoft.Extensions.Configuration.UserSecrets/9.0.10": { + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "9.0.10", + "Microsoft.Extensions.Configuration.Json": "9.0.10", + "Microsoft.Extensions.FileProviders.Abstractions": "9.0.10", + "Microsoft.Extensions.FileProviders.Physical": "9.0.10" + }, + "runtime": { + "lib/net8.0/Microsoft.Extensions.Configuration.UserSecrets.dll": { + "assemblyVersion": "9.0.0.0", + "fileVersion": "9.0.1025.47515" + } + } + }, + "Microsoft.Extensions.DependencyInjection/9.0.10": { + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.10" + }, + "runtime": { + "lib/net8.0/Microsoft.Extensions.DependencyInjection.dll": { + "assemblyVersion": "9.0.0.0", + "fileVersion": "9.0.1025.47515" + } + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions/9.0.10": { + "runtime": { + "lib/net8.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll": { + "assemblyVersion": "9.0.0.0", + "fileVersion": "9.0.1025.47515" + } + } + }, + "Microsoft.Extensions.Diagnostics/9.0.10": { + "dependencies": { + "Microsoft.Extensions.Configuration": "9.0.10", + "Microsoft.Extensions.Diagnostics.Abstractions": "9.0.10", + "Microsoft.Extensions.Options.ConfigurationExtensions": "9.0.10" + }, + "runtime": { + "lib/net8.0/Microsoft.Extensions.Diagnostics.dll": { + "assemblyVersion": "9.0.0.0", + "fileVersion": "9.0.1025.47515" + } + } + }, + "Microsoft.Extensions.Diagnostics.Abstractions/9.0.10": { + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.10", + "Microsoft.Extensions.Options": "9.0.10", + "System.Diagnostics.DiagnosticSource": "9.0.10" + }, + "runtime": { + "lib/net8.0/Microsoft.Extensions.Diagnostics.Abstractions.dll": { + "assemblyVersion": "9.0.0.0", + "fileVersion": "9.0.1025.47515" + } + } + }, + "Microsoft.Extensions.FileProviders.Abstractions/9.0.10": { + "dependencies": { + "Microsoft.Extensions.Primitives": "9.0.10" + }, + "runtime": { + "lib/net8.0/Microsoft.Extensions.FileProviders.Abstractions.dll": { + "assemblyVersion": "9.0.0.0", + "fileVersion": "9.0.1025.47515" + } + } + }, + "Microsoft.Extensions.FileProviders.Physical/9.0.10": { + "dependencies": { + "Microsoft.Extensions.FileProviders.Abstractions": "9.0.10", + "Microsoft.Extensions.FileSystemGlobbing": "9.0.10", + "Microsoft.Extensions.Primitives": "9.0.10" + }, + "runtime": { + "lib/net8.0/Microsoft.Extensions.FileProviders.Physical.dll": { + "assemblyVersion": "9.0.0.0", + "fileVersion": "9.0.1025.47515" + } + } + }, + "Microsoft.Extensions.FileSystemGlobbing/9.0.10": { + "runtime": { + "lib/net8.0/Microsoft.Extensions.FileSystemGlobbing.dll": { + "assemblyVersion": "9.0.0.0", + "fileVersion": "9.0.1025.47515" + } + } + }, + "Microsoft.Extensions.Hosting/9.0.10": { + "dependencies": { + "Microsoft.Extensions.Configuration": "9.0.10", + "Microsoft.Extensions.Configuration.Abstractions": "9.0.10", + "Microsoft.Extensions.Configuration.Binder": "9.0.10", + "Microsoft.Extensions.Configuration.CommandLine": "9.0.10", + "Microsoft.Extensions.Configuration.EnvironmentVariables": "9.0.10", + "Microsoft.Extensions.Configuration.FileExtensions": "9.0.10", + "Microsoft.Extensions.Configuration.Json": "9.0.10", + "Microsoft.Extensions.Configuration.UserSecrets": "9.0.10", + "Microsoft.Extensions.DependencyInjection": "9.0.10", + "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.10", + "Microsoft.Extensions.Diagnostics": "9.0.10", + "Microsoft.Extensions.FileProviders.Abstractions": "9.0.10", + "Microsoft.Extensions.FileProviders.Physical": "9.0.10", + "Microsoft.Extensions.Hosting.Abstractions": "9.0.10", + "Microsoft.Extensions.Logging": "9.0.10", + "Microsoft.Extensions.Logging.Abstractions": "9.0.10", + "Microsoft.Extensions.Logging.Configuration": "9.0.10", + "Microsoft.Extensions.Logging.Console": "9.0.10", + "Microsoft.Extensions.Logging.Debug": "9.0.10", + "Microsoft.Extensions.Logging.EventLog": "9.0.10", + "Microsoft.Extensions.Logging.EventSource": "9.0.10", + "Microsoft.Extensions.Options": "9.0.10" + }, + "runtime": { + "lib/net8.0/Microsoft.Extensions.Hosting.dll": { + "assemblyVersion": "9.0.0.0", + "fileVersion": "9.0.1025.47515" + } + } + }, + "Microsoft.Extensions.Hosting.Abstractions/9.0.10": { + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "9.0.10", + "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.10", + "Microsoft.Extensions.Diagnostics.Abstractions": "9.0.10", + "Microsoft.Extensions.FileProviders.Abstractions": "9.0.10", + "Microsoft.Extensions.Logging.Abstractions": "9.0.10" + }, + "runtime": { + "lib/net8.0/Microsoft.Extensions.Hosting.Abstractions.dll": { + "assemblyVersion": "9.0.0.0", + "fileVersion": "9.0.1025.47515" + } + } + }, + "Microsoft.Extensions.Hosting.WindowsServices/9.0.10": { + "dependencies": { + "Microsoft.Extensions.Hosting": "9.0.10", + "Microsoft.Extensions.Logging.EventLog": "9.0.10", + "System.ServiceProcess.ServiceController": "9.0.10" + }, + "runtime": { + "lib/net8.0/Microsoft.Extensions.Hosting.WindowsServices.dll": { + "assemblyVersion": "9.0.0.10", + "fileVersion": "9.0.1025.47515" + } + } + }, + "Microsoft.Extensions.Http/8.0.0": { + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "9.0.10", + "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.10", + "Microsoft.Extensions.Diagnostics": "9.0.10", + "Microsoft.Extensions.Logging": "9.0.10", + "Microsoft.Extensions.Logging.Abstractions": "9.0.10", + "Microsoft.Extensions.Options": "9.0.10" + } + }, + "Microsoft.Extensions.Logging/9.0.10": { + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "9.0.10", + "Microsoft.Extensions.Logging.Abstractions": "9.0.10", + "Microsoft.Extensions.Options": "9.0.10" + }, + "runtime": { + "lib/net8.0/Microsoft.Extensions.Logging.dll": { + "assemblyVersion": "9.0.0.0", + "fileVersion": "9.0.1025.47515" + } + } + }, + "Microsoft.Extensions.Logging.Abstractions/9.0.10": { + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.10", + "System.Diagnostics.DiagnosticSource": "9.0.10" + }, + "runtime": { + "lib/net8.0/Microsoft.Extensions.Logging.Abstractions.dll": { + "assemblyVersion": "9.0.0.0", + "fileVersion": "9.0.1025.47515" + } + } + }, + "Microsoft.Extensions.Logging.Configuration/9.0.10": { + "dependencies": { + "Microsoft.Extensions.Configuration": "9.0.10", + "Microsoft.Extensions.Configuration.Abstractions": "9.0.10", + "Microsoft.Extensions.Configuration.Binder": "9.0.10", + "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.10", + "Microsoft.Extensions.Logging": "9.0.10", + "Microsoft.Extensions.Logging.Abstractions": "9.0.10", + "Microsoft.Extensions.Options": "9.0.10", + "Microsoft.Extensions.Options.ConfigurationExtensions": "9.0.10" + }, + "runtime": { + "lib/net8.0/Microsoft.Extensions.Logging.Configuration.dll": { + "assemblyVersion": "9.0.0.0", + "fileVersion": "9.0.1025.47515" + } + } + }, + "Microsoft.Extensions.Logging.Console/9.0.10": { + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.10", + "Microsoft.Extensions.Logging": "9.0.10", + "Microsoft.Extensions.Logging.Abstractions": "9.0.10", + "Microsoft.Extensions.Logging.Configuration": "9.0.10", + "Microsoft.Extensions.Options": "9.0.10", + "System.Text.Json": "9.0.10" + }, + "runtime": { + "lib/net8.0/Microsoft.Extensions.Logging.Console.dll": { + "assemblyVersion": "9.0.0.0", + "fileVersion": "9.0.1025.47515" + } + } + }, + "Microsoft.Extensions.Logging.Debug/9.0.10": { + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.10", + "Microsoft.Extensions.Logging": "9.0.10", + "Microsoft.Extensions.Logging.Abstractions": "9.0.10" + }, + "runtime": { + "lib/net8.0/Microsoft.Extensions.Logging.Debug.dll": { + "assemblyVersion": "9.0.0.0", + "fileVersion": "9.0.1025.47515" + } + } + }, + "Microsoft.Extensions.Logging.EventLog/9.0.10": { + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.10", + "Microsoft.Extensions.Logging": "9.0.10", + "Microsoft.Extensions.Logging.Abstractions": "9.0.10", + "Microsoft.Extensions.Options": "9.0.10", + "System.Diagnostics.EventLog": "9.0.10" + }, + "runtime": { + "lib/net8.0/Microsoft.Extensions.Logging.EventLog.dll": { + "assemblyVersion": "9.0.0.0", + "fileVersion": "9.0.1025.47515" + } + } + }, + "Microsoft.Extensions.Logging.EventSource/9.0.10": { + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.10", + "Microsoft.Extensions.Logging": "9.0.10", + "Microsoft.Extensions.Logging.Abstractions": "9.0.10", + "Microsoft.Extensions.Options": "9.0.10", + "Microsoft.Extensions.Primitives": "9.0.10", + "System.Text.Json": "9.0.10" + }, + "runtime": { + "lib/net8.0/Microsoft.Extensions.Logging.EventSource.dll": { + "assemblyVersion": "9.0.0.0", + "fileVersion": "9.0.1025.47515" + } + } + }, + "Microsoft.Extensions.Options/9.0.10": { + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.10", + "Microsoft.Extensions.Primitives": "9.0.10" + }, + "runtime": { + "lib/net8.0/Microsoft.Extensions.Options.dll": { + "assemblyVersion": "9.0.0.0", + "fileVersion": "9.0.1025.47515" + } + } + }, + "Microsoft.Extensions.Options.ConfigurationExtensions/9.0.10": { + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "9.0.10", + "Microsoft.Extensions.Configuration.Binder": "9.0.10", + "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.10", + "Microsoft.Extensions.Options": "9.0.10", + "Microsoft.Extensions.Primitives": "9.0.10" + }, + "runtime": { + "lib/net8.0/Microsoft.Extensions.Options.ConfigurationExtensions.dll": { + "assemblyVersion": "9.0.0.0", + "fileVersion": "9.0.1025.47515" + } + } + }, + "Microsoft.Extensions.Primitives/9.0.10": { + "runtime": { + "lib/net8.0/Microsoft.Extensions.Primitives.dll": { + "assemblyVersion": "9.0.0.0", + "fileVersion": "9.0.1025.47515" + } + } + }, + "Microsoft.OpenApi/1.6.14": { + "runtime": { + "lib/netstandard2.0/Microsoft.OpenApi.dll": { + "assemblyVersion": "1.6.14.0", + "fileVersion": "1.6.14.0" + } + } + }, + "Swashbuckle.AspNetCore/6.6.2": { + "dependencies": { + "Microsoft.Extensions.ApiDescription.Server": "6.0.5", + "Swashbuckle.AspNetCore.Swagger": "6.6.2", + "Swashbuckle.AspNetCore.SwaggerGen": "6.6.2", + "Swashbuckle.AspNetCore.SwaggerUI": "6.6.2" + } + }, + "Swashbuckle.AspNetCore.Swagger/6.6.2": { + "dependencies": { + "Microsoft.OpenApi": "1.6.14" + }, + "runtime": { + "lib/net8.0/Swashbuckle.AspNetCore.Swagger.dll": { + "assemblyVersion": "6.6.2.0", + "fileVersion": "6.6.2.401" + } + } + }, + "Swashbuckle.AspNetCore.SwaggerGen/6.6.2": { + "dependencies": { + "Swashbuckle.AspNetCore.Swagger": "6.6.2" + }, + "runtime": { + "lib/net8.0/Swashbuckle.AspNetCore.SwaggerGen.dll": { + "assemblyVersion": "6.6.2.0", + "fileVersion": "6.6.2.401" + } + } + }, + "Swashbuckle.AspNetCore.SwaggerUI/6.6.2": { + "runtime": { + "lib/net8.0/Swashbuckle.AspNetCore.SwaggerUI.dll": { + "assemblyVersion": "6.6.2.0", + "fileVersion": "6.6.2.401" + } + } + }, + "System.Diagnostics.DiagnosticSource/9.0.10": { + "runtime": { + "lib/net8.0/System.Diagnostics.DiagnosticSource.dll": { + "assemblyVersion": "9.0.0.0", + "fileVersion": "9.0.1025.47515" + } + } + }, + "System.Diagnostics.EventLog/9.0.10": { + "runtime": { + "lib/net8.0/System.Diagnostics.EventLog.dll": { + "assemblyVersion": "9.0.0.0", + "fileVersion": "9.0.1025.47515" + } + }, + "runtimeTargets": { + "runtimes/win/lib/net8.0/System.Diagnostics.EventLog.Messages.dll": { + "rid": "win", + "assetType": "runtime", + "assemblyVersion": "9.0.0.0", + "fileVersion": "0.0.0.0" + }, + "runtimes/win/lib/net8.0/System.Diagnostics.EventLog.dll": { + "rid": "win", + "assetType": "runtime", + "assemblyVersion": "9.0.0.0", + "fileVersion": "9.0.1025.47515" + } + } + }, + "System.IO.Pipelines/9.0.10": { + "runtime": { + "lib/net8.0/System.IO.Pipelines.dll": { + "assemblyVersion": "9.0.0.0", + "fileVersion": "9.0.1025.47515" + } + } + }, + "System.ServiceProcess.ServiceController/9.0.10": { + "dependencies": { + "System.Diagnostics.EventLog": "9.0.10" + }, + "runtime": { + "lib/net8.0/System.ServiceProcess.ServiceController.dll": { + "assemblyVersion": "9.0.0.10", + "fileVersion": "9.0.1025.47515" + } + }, + "runtimeTargets": { + "runtimes/win/lib/net8.0/System.ServiceProcess.ServiceController.dll": { + "rid": "win", + "assetType": "runtime", + "assemblyVersion": "9.0.0.10", + "fileVersion": "9.0.1025.47515" + } + } + }, + "System.Text.Encodings.Web/9.0.10": { + "runtime": { + "lib/net8.0/System.Text.Encodings.Web.dll": { + "assemblyVersion": "9.0.0.0", + "fileVersion": "9.0.1025.47515" + } + }, + "runtimeTargets": { + "runtimes/browser/lib/net8.0/System.Text.Encodings.Web.dll": { + "rid": "browser", + "assetType": "runtime", + "assemblyVersion": "9.0.0.0", + "fileVersion": "9.0.1025.47515" + } + } + }, + "System.Text.Json/9.0.10": { + "dependencies": { + "System.IO.Pipelines": "9.0.10", + "System.Text.Encodings.Web": "9.0.10" + }, + "runtime": { + "lib/net8.0/System.Text.Json.dll": { + "assemblyVersion": "9.0.0.0", + "fileVersion": "9.0.1025.47515" + } + } + } + } + }, + "libraries": { + "RoslynBridge.WebApi/1.0.0": { + "type": "project", + "serviceable": false, + "sha512": "" + }, + "Microsoft.Extensions.ApiDescription.Server/6.0.5": { + "type": "package", + "serviceable": true, + "sha512": "sha512-Ckb5EDBUNJdFWyajfXzUIMRkhf52fHZOQuuZg/oiu8y7zDCVwD0iHhew6MnThjHmevanpxL3f5ci2TtHQEN6bw==", + "path": "microsoft.extensions.apidescription.server/6.0.5", + "hashPath": "microsoft.extensions.apidescription.server.6.0.5.nupkg.sha512" + }, + "Microsoft.Extensions.Configuration/9.0.10": { + "type": "package", + "serviceable": true, + "sha512": "sha512-UAm3SLGAMlJdowbN+/xnh2UGJkdJoXVm4MsdhZ60dAMS8jteoyCx5WfIab5DKv0TCYpdhVecLJVUjEO3abs9UQ==", + "path": "microsoft.extensions.configuration/9.0.10", + "hashPath": "microsoft.extensions.configuration.9.0.10.nupkg.sha512" + }, + "Microsoft.Extensions.Configuration.Abstractions/9.0.10": { + "type": "package", + "serviceable": true, + "sha512": "sha512-ad3JxmFj0uxuFa1CT6oxTCC1lQ0xeRuOvzBRFT/I/ofIXVOnNsH/v2GZkAJWhlpZqKUvSexQZzp3EEAB2CdtJg==", + "path": "microsoft.extensions.configuration.abstractions/9.0.10", + "hashPath": "microsoft.extensions.configuration.abstractions.9.0.10.nupkg.sha512" + }, + "Microsoft.Extensions.Configuration.Binder/9.0.10": { + "type": "package", + "serviceable": true, + "sha512": "sha512-D6Kng+9I+w1SQPxJybc6wzw9nnnyUQPutycjtI0svv1RHaWOpUk9PPlwIRfhhoQZ3yihejkEI2wNv/7VnVtkGA==", + "path": "microsoft.extensions.configuration.binder/9.0.10", + "hashPath": "microsoft.extensions.configuration.binder.9.0.10.nupkg.sha512" + }, + "Microsoft.Extensions.Configuration.CommandLine/9.0.10": { + "type": "package", + "serviceable": true, + "sha512": "sha512-Sg400UyKl33kOpqklEg1MIM3lpY/aWi7QZTB2JfFpKgxnSRQl9J6tHiKYll+Rd603P+71YsDy/zqBYUE/3Xeag==", + "path": "microsoft.extensions.configuration.commandline/9.0.10", + "hashPath": "microsoft.extensions.configuration.commandline.9.0.10.nupkg.sha512" + }, + "Microsoft.Extensions.Configuration.EnvironmentVariables/9.0.10": { + "type": "package", + "serviceable": true, + "sha512": "sha512-Nje8x5JDRi7uzf2q3NpXiBleRRJAxJMnHcJTi0tLyqd6eGIICRuF6qxgZssMS1r8xXDoaUr/2ZLQ6Cui1Io+Qw==", + "path": "microsoft.extensions.configuration.environmentvariables/9.0.10", + "hashPath": "microsoft.extensions.configuration.environmentvariables.9.0.10.nupkg.sha512" + }, + "Microsoft.Extensions.Configuration.FileExtensions/9.0.10": { + "type": "package", + "serviceable": true, + "sha512": "sha512-kYWY9VRoCKQJCLKAA4Wqn74FVnytqosF7vFq1chJ8st9mGZS6SQrkoZg7GmcpqrRRUWmWDOZI4nFdoFnxsI/Ug==", + "path": "microsoft.extensions.configuration.fileextensions/9.0.10", + "hashPath": "microsoft.extensions.configuration.fileextensions.9.0.10.nupkg.sha512" + }, + "Microsoft.Extensions.Configuration.Json/9.0.10": { + "type": "package", + "serviceable": true, + "sha512": "sha512-bn+qnwuOaDelax8PUw30UTjLOuEd0lGWqUG4Z+oVr4D/gEWouCWOyvCVkyn+PWbftPlnmAmWxd4J+7ljwE8wVw==", + "path": "microsoft.extensions.configuration.json/9.0.10", + "hashPath": "microsoft.extensions.configuration.json.9.0.10.nupkg.sha512" + }, + "Microsoft.Extensions.Configuration.UserSecrets/9.0.10": { + "type": "package", + "serviceable": true, + "sha512": "sha512-l7em+qNfEdGlwRm8Qk2rkzqjT8xWb/EosoQeTvJ3kZYiRo5inMj0nNcZw51dUKwGO/LW7uNMdqNNU3P0pB5JqA==", + "path": "microsoft.extensions.configuration.usersecrets/9.0.10", + "hashPath": "microsoft.extensions.configuration.usersecrets.9.0.10.nupkg.sha512" + }, + "Microsoft.Extensions.DependencyInjection/9.0.10": { + "type": "package", + "serviceable": true, + "sha512": "sha512-iEtXCkNd5XhjNJAOb/wO4IhDRdLIE2CsPxZggZQWJ/q2+sa8dmEPC393nnsiqdH8/4KV8Xn25IzgKPR1UEQ0og==", + "path": "microsoft.extensions.dependencyinjection/9.0.10", + "hashPath": "microsoft.extensions.dependencyinjection.9.0.10.nupkg.sha512" + }, + "Microsoft.Extensions.DependencyInjection.Abstractions/9.0.10": { + "type": "package", + "serviceable": true, + "sha512": "sha512-r9waLiOPe9ZF1PvzUT+RDoHvpMmY8MW+lb4lqjYGObwKpnyPMLI3odVvlmshwuZcdoHynsGWOrCPA0hxZ63lIA==", + "path": "microsoft.extensions.dependencyinjection.abstractions/9.0.10", + "hashPath": "microsoft.extensions.dependencyinjection.abstractions.9.0.10.nupkg.sha512" + }, + "Microsoft.Extensions.Diagnostics/9.0.10": { + "type": "package", + "serviceable": true, + "sha512": "sha512-01x2vz0AbIdfNUzEVYFq2HSeq1BmrSDpiG7nTmwjfd0d39sahQ8T7dhSXhH+YnZyaLWyMBudOq0vVa/voyNWjg==", + "path": "microsoft.extensions.diagnostics/9.0.10", + "hashPath": "microsoft.extensions.diagnostics.9.0.10.nupkg.sha512" + }, + "Microsoft.Extensions.Diagnostics.Abstractions/9.0.10": { + "type": "package", + "serviceable": true, + "sha512": "sha512-iwVnYi+gNKrr5riw8YFCoLCN4s0dmHtzfUmV99RIhrz8R4d6C/bsKzXhIhZWDIxJOhVzB+idSOQeRGj1/oMF+Q==", + "path": "microsoft.extensions.diagnostics.abstractions/9.0.10", + "hashPath": "microsoft.extensions.diagnostics.abstractions.9.0.10.nupkg.sha512" + }, + "Microsoft.Extensions.FileProviders.Abstractions/9.0.10": { + "type": "package", + "serviceable": true, + "sha512": "sha512-3+cLxZKUWBbpfIXLLuKcEok9C91PsV1h5xxfUsEnLSXXLNMiPDfrhpb1xajNFcejFPs9Ck/Fi3z71hYDqFBwYg==", + "path": "microsoft.extensions.fileproviders.abstractions/9.0.10", + "hashPath": "microsoft.extensions.fileproviders.abstractions.9.0.10.nupkg.sha512" + }, + "Microsoft.Extensions.FileProviders.Physical/9.0.10": { + "type": "package", + "serviceable": true, + "sha512": "sha512-Eg3YOEMpHWZzAgPD9YvGkQSv97AtG3II6maRQV/voDRORh4bRiyl0mVtT2PKnu1JoD9rJeYgjGCwRvVWMBaqgQ==", + "path": "microsoft.extensions.fileproviders.physical/9.0.10", + "hashPath": "microsoft.extensions.fileproviders.physical.9.0.10.nupkg.sha512" + }, + "Microsoft.Extensions.FileSystemGlobbing/9.0.10": { + "type": "package", + "serviceable": true, + "sha512": "sha512-KdZAM2YMYBipVp/4tSEWPLnrocd17SL4iaXdgXjR5/nheBXbfR5QfPWYoTyh6C6IW3uKR7TRMwQr2qCvtaCTiA==", + "path": "microsoft.extensions.filesystemglobbing/9.0.10", + "hashPath": "microsoft.extensions.filesystemglobbing.9.0.10.nupkg.sha512" + }, + "Microsoft.Extensions.Hosting/9.0.10": { + "type": "package", + "serviceable": true, + "sha512": "sha512-63yDlitelCBNl1unJsnEWVUCZHOtxbVTbTODi7cszQJBG9bIfdPYIpB9w0UIcoqVSP1C9P6THXgukx8APWRzMw==", + "path": "microsoft.extensions.hosting/9.0.10", + "hashPath": "microsoft.extensions.hosting.9.0.10.nupkg.sha512" + }, + "Microsoft.Extensions.Hosting.Abstractions/9.0.10": { + "type": "package", + "serviceable": true, + "sha512": "sha512-spfXydiEQENFwxdgr3Y57wwys/FRjfmq5VjHGPh6ct1FJK7X+qNEWYbnZJCMqq0B0oJTMvnItAReOv4mi2Idog==", + "path": "microsoft.extensions.hosting.abstractions/9.0.10", + "hashPath": "microsoft.extensions.hosting.abstractions.9.0.10.nupkg.sha512" + }, + "Microsoft.Extensions.Hosting.WindowsServices/9.0.10": { + "type": "package", + "serviceable": true, + "sha512": "sha512-6J44fDEcblCFoc7vNwuxLQrfY9ldToc2pkfzuraCOCsRAVFg4Fajm+PdpDYQ3VzG7eh1Iqc1JMPs9LMpaIeSKA==", + "path": "microsoft.extensions.hosting.windowsservices/9.0.10", + "hashPath": "microsoft.extensions.hosting.windowsservices.9.0.10.nupkg.sha512" + }, + "Microsoft.Extensions.Http/8.0.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-cWz4caHwvx0emoYe7NkHPxII/KkTI8R/LC9qdqJqnKv2poTJ4e2qqPGQqvRoQ5kaSA4FU5IV3qFAuLuOhoqULQ==", + "path": "microsoft.extensions.http/8.0.0", + "hashPath": "microsoft.extensions.http.8.0.0.nupkg.sha512" + }, + "Microsoft.Extensions.Logging/9.0.10": { + "type": "package", + "serviceable": true, + "sha512": "sha512-UBXHqE9vyptVhaFnT1R7YJKCve7TqVI10yjjUZBNGMlW2lZ4c031Slt9hxsOzWCzlpPxxIFyf1Yk4a6Iubxx7w==", + "path": "microsoft.extensions.logging/9.0.10", + "hashPath": "microsoft.extensions.logging.9.0.10.nupkg.sha512" + }, + "Microsoft.Extensions.Logging.Abstractions/9.0.10": { + "type": "package", + "serviceable": true, + "sha512": "sha512-MFUPv/nN1rAQ19w43smm6bbf0JDYN/1HEPHoiMYY50pvDMFpglzWAuoTavByDmZq7UuhjaxwrET3joU69ZHoHQ==", + "path": "microsoft.extensions.logging.abstractions/9.0.10", + "hashPath": "microsoft.extensions.logging.abstractions.9.0.10.nupkg.sha512" + }, + "Microsoft.Extensions.Logging.Configuration/9.0.10": { + "type": "package", + "serviceable": true, + "sha512": "sha512-qwTRpxrmLOXZrbgQHRZ9wS2AtVa/61DFIYk8k1rBCCgA5qW0MBxxQC4BjkaI0wSoHHOv/IUXBeFNK+Y59qe/Ug==", + "path": "microsoft.extensions.logging.configuration/9.0.10", + "hashPath": "microsoft.extensions.logging.configuration.9.0.10.nupkg.sha512" + }, + "Microsoft.Extensions.Logging.Console/9.0.10": { + "type": "package", + "serviceable": true, + "sha512": "sha512-ponA8k4E4S0LlQ8J4ce4Yp1NND8rxww0lbADK9yL3omRpnnawiENb7W/CTgZUIZVJxKcmIwhm1IbUCRk6RLocQ==", + "path": "microsoft.extensions.logging.console/9.0.10", + "hashPath": "microsoft.extensions.logging.console.9.0.10.nupkg.sha512" + }, + "Microsoft.Extensions.Logging.Debug/9.0.10": { + "type": "package", + "serviceable": true, + "sha512": "sha512-Uj4YMaMMLawIkpHYnDWsR2/pufV/8X3dDT1/RNhkmt8RRf6/SriyA2gxH6I6bj4gFx6yMuFWZhCgFLy3wcSGTw==", + "path": "microsoft.extensions.logging.debug/9.0.10", + "hashPath": "microsoft.extensions.logging.debug.9.0.10.nupkg.sha512" + }, + "Microsoft.Extensions.Logging.EventLog/9.0.10": { + "type": "package", + "serviceable": true, + "sha512": "sha512-Son+9zr7gnuYv1CcuZ8b6XhZK/UQRG88Ku1iSUvAQSZ1cFjYC+lDYRD6nBVXF2QIQyv0jhjt/MPKD7sA+323TQ==", + "path": "microsoft.extensions.logging.eventlog/9.0.10", + "hashPath": "microsoft.extensions.logging.eventlog.9.0.10.nupkg.sha512" + }, + "Microsoft.Extensions.Logging.EventSource/9.0.10": { + "type": "package", + "serviceable": true, + "sha512": "sha512-qhqkUWsf/CVyQ9V98n5uWSQcvy7HbyRkhyhpK75OKojWuaNKoEIfBmrHRiahmdGJDuh2Qz/nDpFOjQOi/ERtZQ==", + "path": "microsoft.extensions.logging.eventsource/9.0.10", + "hashPath": "microsoft.extensions.logging.eventsource.9.0.10.nupkg.sha512" + }, + "Microsoft.Extensions.Options/9.0.10": { + "type": "package", + "serviceable": true, + "sha512": "sha512-zMNABt8eBv0B0XrWjFy9nZNgddavaOeq3ZdaD5IlHhRH65MrU7HM+Hd8GjWE3e2VDGFPZFfSAc6XVXC17f9fOA==", + "path": "microsoft.extensions.options/9.0.10", + "hashPath": "microsoft.extensions.options.9.0.10.nupkg.sha512" + }, + "Microsoft.Extensions.Options.ConfigurationExtensions/9.0.10": { + "type": "package", + "serviceable": true, + "sha512": "sha512-wLsf2TyVFFxWQPv0PRJj365it1ngIt8utlHJWSZ9OJ2k+NDa/PtBIRsGlF/NkoLwm1m+1vOePNl2MiKfk6lYfQ==", + "path": "microsoft.extensions.options.configurationextensions/9.0.10", + "hashPath": "microsoft.extensions.options.configurationextensions.9.0.10.nupkg.sha512" + }, + "Microsoft.Extensions.Primitives/9.0.10": { + "type": "package", + "serviceable": true, + "sha512": "sha512-3pl8D1O5ZwMpDkZAT2uXrhQ6NipkwEgDLMFuURiHTf72TvkoMP61QYH3Vk1yrzVHnHBdNZk3cQACz8Zc7YGNhQ==", + "path": "microsoft.extensions.primitives/9.0.10", + "hashPath": "microsoft.extensions.primitives.9.0.10.nupkg.sha512" + }, + "Microsoft.OpenApi/1.6.14": { + "type": "package", + "serviceable": true, + "sha512": "sha512-tTaBT8qjk3xINfESyOPE2rIellPvB7qpVqiWiyA/lACVvz+xOGiXhFUfohcx82NLbi5avzLW0lx+s6oAqQijfw==", + "path": "microsoft.openapi/1.6.14", + "hashPath": "microsoft.openapi.1.6.14.nupkg.sha512" + }, + "Swashbuckle.AspNetCore/6.6.2": { + "type": "package", + "serviceable": true, + "sha512": "sha512-+NB4UYVYN6AhDSjW0IJAd1AGD8V33gemFNLPaxKTtPkHB+HaKAKf9MGAEUPivEWvqeQfcKIw8lJaHq6LHljRuw==", + "path": "swashbuckle.aspnetcore/6.6.2", + "hashPath": "swashbuckle.aspnetcore.6.6.2.nupkg.sha512" + }, + "Swashbuckle.AspNetCore.Swagger/6.6.2": { + "type": "package", + "serviceable": true, + "sha512": "sha512-ovgPTSYX83UrQUWiS5vzDcJ8TEX1MAxBgDFMK45rC24MorHEPQlZAHlaXj/yth4Zf6xcktpUgTEBvffRQVwDKA==", + "path": "swashbuckle.aspnetcore.swagger/6.6.2", + "hashPath": "swashbuckle.aspnetcore.swagger.6.6.2.nupkg.sha512" + }, + "Swashbuckle.AspNetCore.SwaggerGen/6.6.2": { + "type": "package", + "serviceable": true, + "sha512": "sha512-zv4ikn4AT1VYuOsDCpktLq4QDq08e7Utzbir86M5/ZkRaLXbCPF11E1/vTmOiDzRTl0zTZINQU2qLKwTcHgfrA==", + "path": "swashbuckle.aspnetcore.swaggergen/6.6.2", + "hashPath": "swashbuckle.aspnetcore.swaggergen.6.6.2.nupkg.sha512" + }, + "Swashbuckle.AspNetCore.SwaggerUI/6.6.2": { + "type": "package", + "serviceable": true, + "sha512": "sha512-mBBb+/8Hm2Q3Wygag+hu2jj69tZW5psuv0vMRXY07Wy+Rrj40vRP8ZTbKBhs91r45/HXT4aY4z0iSBYx1h6JvA==", + "path": "swashbuckle.aspnetcore.swaggerui/6.6.2", + "hashPath": "swashbuckle.aspnetcore.swaggerui.6.6.2.nupkg.sha512" + }, + "System.Diagnostics.DiagnosticSource/9.0.10": { + "type": "package", + "serviceable": true, + "sha512": "sha512-uIpKiKp7EWlYZBK71jYP+maGYjDY9YTi/FxBlZoqDzM1ZHZB7gLqUm4jHvRFwaKfR1/Lrt2rQih9LGPIKyNEow==", + "path": "system.diagnostics.diagnosticsource/9.0.10", + "hashPath": "system.diagnostics.diagnosticsource.9.0.10.nupkg.sha512" + }, + "System.Diagnostics.EventLog/9.0.10": { + "type": "package", + "serviceable": true, + "sha512": "sha512-Jc+az1pTMujPLDn2j5eqSfzlO7j/T1K/LB7THxdfRWOxujE4zaitUqBs7sv1t6/xmmvpU6Xx3IofCs4owYH0yQ==", + "path": "system.diagnostics.eventlog/9.0.10", + "hashPath": "system.diagnostics.eventlog.9.0.10.nupkg.sha512" + }, + "System.IO.Pipelines/9.0.10": { + "type": "package", + "serviceable": true, + "sha512": "sha512-lwI0mhHcCxMtNSxB5ate9Gc9petWovRBUprtjz2yiIDDZPGBIaUiqNzQHJzjPuzTnvNbEMilpAXjDguKsU/2Fg==", + "path": "system.io.pipelines/9.0.10", + "hashPath": "system.io.pipelines.9.0.10.nupkg.sha512" + }, + "System.ServiceProcess.ServiceController/9.0.10": { + "type": "package", + "serviceable": true, + "sha512": "sha512-dmH+qHQ5wMjvEI0M2s6J+vmaU9L9ID2D9DWMFa7FiTfINfo3e3zeL4ljX7Dg5gCnFIULPFip2ej2iIAC3X6MFw==", + "path": "system.serviceprocess.servicecontroller/9.0.10", + "hashPath": "system.serviceprocess.servicecontroller.9.0.10.nupkg.sha512" + }, + "System.Text.Encodings.Web/9.0.10": { + "type": "package", + "serviceable": true, + "sha512": "sha512-znmiJFUa0GGwq7t6ShUKBDRlPsNJaudNFI7rVeyGnRBhiRMegBvu2GRcadThP/QX/a5UpGgZbe6tolDooobj/Q==", + "path": "system.text.encodings.web/9.0.10", + "hashPath": "system.text.encodings.web.9.0.10.nupkg.sha512" + }, + "System.Text.Json/9.0.10": { + "type": "package", + "serviceable": true, + "sha512": "sha512-XM02ZBnzxk7Ti6l9YRy8Bp639wANqJzJzw4W4VYiCh+HXY9hBOWkGB4k89OLP/s/RxgM02P4a/mWcJTgFiLf1Q==", + "path": "system.text.json/9.0.10", + "hashPath": "system.text.json.9.0.10.nupkg.sha512" + } + } +} \ No newline at end of file diff --git a/RoslynBridge.WebApi/publish/RoslynBridge.WebApi.dll b/RoslynBridge.WebApi/publish/RoslynBridge.WebApi.dll new file mode 100644 index 0000000..30a4d30 Binary files /dev/null and b/RoslynBridge.WebApi/publish/RoslynBridge.WebApi.dll differ diff --git a/RoslynBridge.WebApi/publish/RoslynBridge.WebApi.runtimeconfig.json b/RoslynBridge.WebApi/publish/RoslynBridge.WebApi.runtimeconfig.json new file mode 100644 index 0000000..6a48a7e --- /dev/null +++ b/RoslynBridge.WebApi/publish/RoslynBridge.WebApi.runtimeconfig.json @@ -0,0 +1,20 @@ +{ + "runtimeOptions": { + "tfm": "net8.0", + "frameworks": [ + { + "name": "Microsoft.NETCore.App", + "version": "8.0.0" + }, + { + "name": "Microsoft.AspNetCore.App", + "version": "8.0.0" + } + ], + "configProperties": { + "System.GC.Server": true, + "System.Reflection.Metadata.MetadataUpdater.IsSupported": false, + "System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false + } + } +} \ No newline at end of file diff --git a/RoslynBridge.WebApi/publish/RoslynBridge.WebApi.staticwebassets.endpoints.json b/RoslynBridge.WebApi/publish/RoslynBridge.WebApi.staticwebassets.endpoints.json new file mode 100644 index 0000000..21da96b --- /dev/null +++ b/RoslynBridge.WebApi/publish/RoslynBridge.WebApi.staticwebassets.endpoints.json @@ -0,0 +1 @@ +{"Version":1,"ManifestType":"Publish","Endpoints":[]} \ No newline at end of file diff --git a/RoslynBridge.WebApi/publish/RoslynBridge.WebApi.xml b/RoslynBridge.WebApi/publish/RoslynBridge.WebApi.xml new file mode 100644 index 0000000..f7793e4 --- /dev/null +++ b/RoslynBridge.WebApi/publish/RoslynBridge.WebApi.xml @@ -0,0 +1,576 @@ + + + + RoslynBridge.WebApi + + + + + Health check and status controller + + + + + Get the health status of the middleware and Visual Studio plugin + + Cancellation token + Health status information + Service is healthy + Service is unhealthy + + + + Simple ping endpoint + + Pong response + + + + Controller for accessing query history + + + + + Get all history entries + + List of all history entries + Returns the list of history entries + + + + Get a specific history entry by ID + + The history entry ID + The history entry + Returns the history entry + Entry not found + + + + Get recent history entries + + Number of entries to return (default: 50, max: 500) + List of recent history entries + Returns the list of recent entries + + + + Get history statistics + + Statistics about history entries + + + + Clear all history entries + + Confirmation message + History cleared successfully + + + + Controller for managing Visual Studio instance registrations + + + + + Register a new Visual Studio instance + + Instance information + Registration result + + + + Unregister a Visual Studio instance + + Process ID of the instance to unregister + Unregistration result + + + + Update heartbeat for a Visual Studio instance + + Process ID of the instance + Heartbeat update result + + + + Get all registered Visual Studio instances + + List of registered instances + + + + Get instance by process ID + + Process ID + Instance information + + + + Get instance by solution path + + Solution file path + Instance information + + + + Get instance by port + + Port number + Instance information + + + + Controller for Roslyn code analysis operations + + + + + Execute a Roslyn query + + The query request + Optional: specific VS instance port to target + Cancellation token + The query result + Query executed successfully + Invalid request + Internal server error + + + + Get all projects in the solution + + Optional: specific VS instance port to target + Cancellation token + List of projects + + + + Get solution overview + + Optional: specific VS instance port to target + Cancellation token + Solution statistics and overview + + + + Get diagnostics (errors and warnings) + + Optional file path to filter diagnostics + Optional: specific VS instance port to target + Cancellation token + List of diagnostics + + + + Get symbol information at a specific position + + File path + Line number (1-based) + Column number (0-based) + Optional: specific VS instance port to target + Cancellation token + Symbol information + + + + Find all references to a symbol + + File path + Line number (1-based) + Column number (0-based) + Optional: specific VS instance port to target + Cancellation token + List of references + + + + Search for symbols by name + + Symbol name or pattern + Optional symbol kind filter + Optional: specific VS instance port to target + Cancellation token + List of matching symbols + + + + Format a document + + File path to format + Optional: specific VS instance port to target + Cancellation token + Format operation result + + + + Add a NuGet package to a project + + Project name + NuGet package name + Optional package version + Optional: specific VS instance port to target + Cancellation token + Operation result + + + + Build a project + + Project name + Build configuration (Debug/Release) + Optional: specific VS instance port to target + Cancellation token + Build result + + + + Middleware to capture and log all Roslyn API requests and responses + + + + + Extension methods for adding history middleware + + + + + Health check response for service status + + + + + Overall health status + + + + + Web API service status + + + + + Visual Studio plugin connection status + + + + + Timestamp of the health check + + + + + API version + + + + + Represents a single query history entry with request and response information + + + + + Unique identifier for this history entry + + + + + Timestamp when the request was received + + + + + The endpoint path that was called + + + + + HTTP method used + + + + + The Roslyn query request + + + + + The Roslyn query response + + + + + Request duration in milliseconds + + + + + Whether the request was successful + + + + + Client IP address + + + + + Request model for Roslyn query operations + + + + + Type of query to execute (e.g., "getsymbol", "getdocument", "findreferences") + + + + + File path for file-based operations + + + + + Symbol name for symbol-based operations + + + + + Line number (1-based) for position-based operations + + + + + Column number (0-based) for position-based operations + + + + + Additional parameters for the query + + + + + Project name for project operations + + + + + Package name for NuGet operations + + + + + Version for NuGet package operations + + + + + Build configuration (Debug/Release) + + + + + Directory path for directory operations + + + + + Response model for Roslyn query operations + + + + + Indicates whether the operation was successful + + + + + Optional message providing additional context + + + + + The response data (structure varies by query type) + + + + + Error message if the operation failed + + + + + Information about a registered Visual Studio instance + + + + + The port number where this VS instance is listening + + + + + The process ID of the Visual Studio instance + + + + + The solution file path (if any solution is open) + + + + + The solution name (if any solution is open) + + + + + When this instance was registered + + + + + Last heartbeat time + + + + + List of project names in the solution + + + + + In-memory implementation of history service + + + + + Service for managing query history + + + + + Add a new history entry + + The history entry to add + + + + Get all history entries + + List of all history entries + + + + Get a specific history entry by ID + + The entry ID + The history entry, or null if not found + + + + Get recent history entries + + Number of entries to return + List of recent history entries + + + + Clear all history entries + + + + + Get total count of history entries + + Total number of entries + + + + Service for managing registered Visual Studio instances + + + + + Register a new Visual Studio instance + + + + + Unregister a Visual Studio instance by process ID + + + + + Update heartbeat for an instance + + + + + Get all registered instances + + + + + Get instance by process ID + + + + + Get instance by solution path + + + + + Get instance by port + + + + + Remove stale instances (no heartbeat for specified timeout) + + + + + Background service that periodically removes stale VS instances + + + + + Thread-safe in-memory registry for Visual Studio instances + + + + + Interface for communicating with the Roslyn Bridge Visual Studio plugin + + + + + Execute a query against the Roslyn Bridge server + + The query request + Optional port of specific VS instance to target + Cancellation token + The query response + + + + Check if the Roslyn Bridge server is healthy + + Optional port of specific VS instance to check + Cancellation token + True if healthy, false otherwise + + + + HTTP client for communicating with the Roslyn Bridge Visual Studio plugin + + + + + Resolves which VS instance port to use based on provided hints + + + + diff --git a/RoslynBridge.WebApi/publish/Swashbuckle.AspNetCore.Swagger.dll b/RoslynBridge.WebApi/publish/Swashbuckle.AspNetCore.Swagger.dll new file mode 100644 index 0000000..41e2fc2 Binary files /dev/null and b/RoslynBridge.WebApi/publish/Swashbuckle.AspNetCore.Swagger.dll differ diff --git a/RoslynBridge.WebApi/publish/Swashbuckle.AspNetCore.SwaggerGen.dll b/RoslynBridge.WebApi/publish/Swashbuckle.AspNetCore.SwaggerGen.dll new file mode 100644 index 0000000..de7f45d Binary files /dev/null and b/RoslynBridge.WebApi/publish/Swashbuckle.AspNetCore.SwaggerGen.dll differ diff --git a/RoslynBridge.WebApi/publish/Swashbuckle.AspNetCore.SwaggerUI.dll b/RoslynBridge.WebApi/publish/Swashbuckle.AspNetCore.SwaggerUI.dll new file mode 100644 index 0000000..117b9f3 Binary files /dev/null and b/RoslynBridge.WebApi/publish/Swashbuckle.AspNetCore.SwaggerUI.dll differ diff --git a/RoslynBridge.WebApi/publish/System.Diagnostics.DiagnosticSource.dll b/RoslynBridge.WebApi/publish/System.Diagnostics.DiagnosticSource.dll new file mode 100644 index 0000000..535d956 Binary files /dev/null and b/RoslynBridge.WebApi/publish/System.Diagnostics.DiagnosticSource.dll differ diff --git a/RoslynBridge.WebApi/publish/System.Diagnostics.EventLog.dll b/RoslynBridge.WebApi/publish/System.Diagnostics.EventLog.dll new file mode 100644 index 0000000..92a5c74 Binary files /dev/null and b/RoslynBridge.WebApi/publish/System.Diagnostics.EventLog.dll differ diff --git a/RoslynBridge.WebApi/publish/System.IO.Pipelines.dll b/RoslynBridge.WebApi/publish/System.IO.Pipelines.dll new file mode 100644 index 0000000..12160e0 Binary files /dev/null and b/RoslynBridge.WebApi/publish/System.IO.Pipelines.dll differ diff --git a/RoslynBridge.WebApi/publish/System.ServiceProcess.ServiceController.dll b/RoslynBridge.WebApi/publish/System.ServiceProcess.ServiceController.dll new file mode 100644 index 0000000..5adc56d Binary files /dev/null and b/RoslynBridge.WebApi/publish/System.ServiceProcess.ServiceController.dll differ diff --git a/RoslynBridge.WebApi/publish/System.Text.Encodings.Web.dll b/RoslynBridge.WebApi/publish/System.Text.Encodings.Web.dll new file mode 100644 index 0000000..596a73f Binary files /dev/null and b/RoslynBridge.WebApi/publish/System.Text.Encodings.Web.dll differ diff --git a/RoslynBridge.WebApi/publish/System.Text.Json.dll b/RoslynBridge.WebApi/publish/System.Text.Json.dll new file mode 100644 index 0000000..66e9b6d Binary files /dev/null and b/RoslynBridge.WebApi/publish/System.Text.Json.dll differ diff --git a/RoslynBridge.WebApi/publish/appsettings.Development.json b/RoslynBridge.WebApi/publish/appsettings.Development.json new file mode 100644 index 0000000..d9e464b --- /dev/null +++ b/RoslynBridge.WebApi/publish/appsettings.Development.json @@ -0,0 +1,12 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning", + "RoslynBridge.WebApi": "Debug" + } + }, + "RoslynBridge": { + "BaseUrl": "http://localhost:59123" + } +} diff --git a/RoslynBridge.WebApi/publish/appsettings.json b/RoslynBridge.WebApi/publish/appsettings.json new file mode 100644 index 0000000..c152051 --- /dev/null +++ b/RoslynBridge.WebApi/publish/appsettings.json @@ -0,0 +1,33 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning", + "RoslynBridge.WebApi": "Information" + }, + "EventLog": { + "SourceName": "Roslyn Bridge Web API", + "LogName": "Application", + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } + }, + "AllowedHosts": "*", + "RoslynBridge": { + "BaseUrl": "http://localhost:59123", + "TimeoutSeconds": 30 + }, + "History": { + "MaxEntries": 1000, + "Enabled": true + }, + "Kestrel": { + "Endpoints": { + "Http": { + "Url": "http://localhost:5000" + } + } + } +} diff --git a/RoslynBridge.WebApi/publish/runtimes/browser/lib/net8.0/System.Text.Encodings.Web.dll b/RoslynBridge.WebApi/publish/runtimes/browser/lib/net8.0/System.Text.Encodings.Web.dll new file mode 100644 index 0000000..0e075ed Binary files /dev/null and b/RoslynBridge.WebApi/publish/runtimes/browser/lib/net8.0/System.Text.Encodings.Web.dll differ diff --git a/RoslynBridge.WebApi/publish/runtimes/win/lib/net8.0/System.Diagnostics.EventLog.Messages.dll b/RoslynBridge.WebApi/publish/runtimes/win/lib/net8.0/System.Diagnostics.EventLog.Messages.dll new file mode 100644 index 0000000..893d4e9 Binary files /dev/null and b/RoslynBridge.WebApi/publish/runtimes/win/lib/net8.0/System.Diagnostics.EventLog.Messages.dll differ diff --git a/RoslynBridge.WebApi/publish/runtimes/win/lib/net8.0/System.Diagnostics.EventLog.dll b/RoslynBridge.WebApi/publish/runtimes/win/lib/net8.0/System.Diagnostics.EventLog.dll new file mode 100644 index 0000000..1f645a4 Binary files /dev/null and b/RoslynBridge.WebApi/publish/runtimes/win/lib/net8.0/System.Diagnostics.EventLog.dll differ diff --git a/RoslynBridge.WebApi/publish/runtimes/win/lib/net8.0/System.ServiceProcess.ServiceController.dll b/RoslynBridge.WebApi/publish/runtimes/win/lib/net8.0/System.ServiceProcess.ServiceController.dll new file mode 100644 index 0000000..9156521 Binary files /dev/null and b/RoslynBridge.WebApi/publish/runtimes/win/lib/net8.0/System.ServiceProcess.ServiceController.dll differ diff --git a/RoslynBridge.WebApi/publish/web.config b/RoslynBridge.WebApi/publish/web.config new file mode 100644 index 0000000..03a7f65 --- /dev/null +++ b/RoslynBridge.WebApi/publish/web.config @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/RoslynBridge.sln b/RoslynBridge.sln index 1daf1bb..cd67d19 100644 --- a/RoslynBridge.sln +++ b/RoslynBridge.sln @@ -4,6 +4,8 @@ VisualStudioVersion = 17.0.31903.59 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RoslynBridge", "RoslynBridge\RoslynBridge.csproj", "{B2C3D4E5-F6A7-4B5C-9D8E-0F1A2B3C4D5E}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RoslynBridge.WebApi", "RoslynBridge.WebApi\RoslynBridge.WebApi.csproj", "{F257A158-E5B4-569A-2B64-6EA9157F9C52}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -14,6 +16,10 @@ Global {B2C3D4E5-F6A7-4B5C-9D8E-0F1A2B3C4D5E}.Debug|Any CPU.Build.0 = Debug|Any CPU {B2C3D4E5-F6A7-4B5C-9D8E-0F1A2B3C4D5E}.Release|Any CPU.ActiveCfg = Release|Any CPU {B2C3D4E5-F6A7-4B5C-9D8E-0F1A2B3C4D5E}.Release|Any CPU.Build.0 = Release|Any CPU + {F257A158-E5B4-569A-2B64-6EA9157F9C52}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F257A158-E5B4-569A-2B64-6EA9157F9C52}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F257A158-E5B4-569A-2B64-6EA9157F9C52}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F257A158-E5B4-569A-2B64-6EA9157F9C52}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE