Add WebAPI middleware for multi-instance Roslyn Bridge routing

Add RoslynBridge.WebApi - ASP.NET Core 8.0 middleware that:
- Provides a centralized REST API for accessing multiple VS instances
- Manages instance registry with discovery by port, solution, or PID
- Proxies requests to the appropriate VS instance
- Tracks request/response history for debugging
- Auto-cleanup of stale instances via background service

Features:
- Health endpoints: /api/health, /api/health/ping
- Roslyn endpoints: /api/roslyn/projects, /api/roslyn/diagnostics, etc.
- Instance management: /api/instances (register, heartbeat, unregister)
- History tracking: /api/history, /api/history/stats
- Swagger UI at root (/) for API documentation
- CORS enabled for web applications

Services:
- InstanceRegistryService: Thread-safe registry of VS instances
- HistoryService: In-memory request/response history (max 1000 entries)
- InstanceCleanupService: Background service to remove stale instances
- RoslynBridgeClient: HTTP client for proxying to VS instances

Update RoslynBridge.sln to include RoslynBridge.WebApi project.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-10-26 23:51:33 -04:00
parent 716827a665
commit 1cbfba3893
76 changed files with 4322 additions and 0 deletions

View File

@@ -0,0 +1,72 @@
using Microsoft.AspNetCore.Mvc;
using RoslynBridge.WebApi.Models;
using RoslynBridge.WebApi.Services;
namespace RoslynBridge.WebApi.Controllers;
/// <summary>
/// Health check and status controller
/// </summary>
[ApiController]
[Route("api/[controller]")]
[Produces("application/json")]
public class HealthController : ControllerBase
{
private readonly IRoslynBridgeClient _bridgeClient;
private readonly ILogger<HealthController> _logger;
public HealthController(IRoslynBridgeClient bridgeClient, ILogger<HealthController> logger)
{
_bridgeClient = bridgeClient;
_logger = logger;
}
/// <summary>
/// Get the health status of the middleware and Visual Studio plugin
/// </summary>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>Health status information</returns>
/// <response code="200">Service is healthy</response>
/// <response code="503">Service is unhealthy</response>
[HttpGet]
[ProducesResponseType(typeof(HealthCheckResponse), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(HealthCheckResponse), StatusCodes.Status503ServiceUnavailable)]
public async Task<ActionResult<HealthCheckResponse>> 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);
}
}
/// <summary>
/// Simple ping endpoint
/// </summary>
/// <returns>Pong response</returns>
[HttpGet("ping")]
[ProducesResponseType(typeof(object), StatusCodes.Status200OK)]
public IActionResult Ping()
{
return Ok(new { message = "pong", timestamp = DateTime.UtcNow });
}
}

View File

@@ -0,0 +1,117 @@
using Microsoft.AspNetCore.Mvc;
using RoslynBridge.WebApi.Models;
using RoslynBridge.WebApi.Services;
namespace RoslynBridge.WebApi.Controllers;
/// <summary>
/// Controller for accessing query history
/// </summary>
[ApiController]
[Route("api/[controller]")]
[Produces("application/json")]
public class HistoryController : ControllerBase
{
private readonly IHistoryService _historyService;
private readonly ILogger<HistoryController> _logger;
public HistoryController(IHistoryService historyService, ILogger<HistoryController> logger)
{
_historyService = historyService;
_logger = logger;
}
/// <summary>
/// Get all history entries
/// </summary>
/// <returns>List of all history entries</returns>
/// <response code="200">Returns the list of history entries</response>
[HttpGet]
[ProducesResponseType(typeof(IEnumerable<QueryHistoryEntry>), StatusCodes.Status200OK)]
public ActionResult<IEnumerable<QueryHistoryEntry>> GetAll()
{
var entries = _historyService.GetAll();
return Ok(entries);
}
/// <summary>
/// Get a specific history entry by ID
/// </summary>
/// <param name="id">The history entry ID</param>
/// <returns>The history entry</returns>
/// <response code="200">Returns the history entry</response>
/// <response code="404">Entry not found</response>
[HttpGet("{id}")]
[ProducesResponseType(typeof(QueryHistoryEntry), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult<QueryHistoryEntry> GetById(string id)
{
var entry = _historyService.GetById(id);
if (entry == null)
{
return NotFound(new { message = $"History entry {id} not found" });
}
return Ok(entry);
}
/// <summary>
/// Get recent history entries
/// </summary>
/// <param name="count">Number of entries to return (default: 50, max: 500)</param>
/// <returns>List of recent history entries</returns>
/// <response code="200">Returns the list of recent entries</response>
[HttpGet("recent")]
[ProducesResponseType(typeof(IEnumerable<QueryHistoryEntry>), StatusCodes.Status200OK)]
public ActionResult<IEnumerable<QueryHistoryEntry>> GetRecent([FromQuery] int count = 50)
{
if (count > 500) count = 500;
if (count < 1) count = 1;
var entries = _historyService.GetRecent(count);
return Ok(entries);
}
/// <summary>
/// Get history statistics
/// </summary>
/// <returns>Statistics about history entries</returns>
[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);
}
/// <summary>
/// Clear all history entries
/// </summary>
/// <returns>Confirmation message</returns>
/// <response code="200">History cleared successfully</response>
[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" });
}
}

View File

@@ -0,0 +1,168 @@
using Microsoft.AspNetCore.Mvc;
using RoslynBridge.WebApi.Models;
using RoslynBridge.WebApi.Services;
namespace RoslynBridge.WebApi.Controllers;
/// <summary>
/// Controller for managing Visual Studio instance registrations
/// </summary>
[ApiController]
[Route("api/[controller]")]
[Produces("application/json")]
public class InstancesController : ControllerBase
{
private readonly IInstanceRegistryService _registryService;
private readonly ILogger<InstancesController> _logger;
public InstancesController(
IInstanceRegistryService registryService,
ILogger<InstancesController> logger)
{
_registryService = registryService;
_logger = logger;
}
/// <summary>
/// Register a new Visual Studio instance
/// </summary>
/// <param name="instance">Instance information</param>
/// <returns>Registration result</returns>
[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
});
}
/// <summary>
/// Unregister a Visual Studio instance
/// </summary>
/// <param name="processId">Process ID of the instance to unregister</param>
/// <returns>Unregistration result</returns>
[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" });
}
/// <summary>
/// Update heartbeat for a Visual Studio instance
/// </summary>
/// <param name="processId">Process ID of the instance</param>
/// <returns>Heartbeat update result</returns>
[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" });
}
/// <summary>
/// Get all registered Visual Studio instances
/// </summary>
/// <returns>List of registered instances</returns>
[HttpGet]
[ProducesResponseType(typeof(IEnumerable<VSInstanceInfo>), StatusCodes.Status200OK)]
public IActionResult GetAll()
{
var instances = _registryService.GetAllInstances();
return Ok(instances);
}
/// <summary>
/// Get instance by process ID
/// </summary>
/// <param name="processId">Process ID</param>
/// <returns>Instance information</returns>
[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);
}
/// <summary>
/// Get instance by solution path
/// </summary>
/// <param name="solutionPath">Solution file path</param>
/// <returns>Instance information</returns>
[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);
}
/// <summary>
/// Get instance by port
/// </summary>
/// <param name="port">Port number</param>
/// <returns>Instance information</returns>
[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);
}
}

View File

@@ -0,0 +1,283 @@
using Microsoft.AspNetCore.Mvc;
using RoslynBridge.WebApi.Models;
using RoslynBridge.WebApi.Services;
namespace RoslynBridge.WebApi.Controllers;
/// <summary>
/// Controller for Roslyn code analysis operations
/// </summary>
[ApiController]
[Route("api/[controller]")]
[Produces("application/json")]
public class RoslynController : ControllerBase
{
private readonly IRoslynBridgeClient _bridgeClient;
private readonly ILogger<RoslynController> _logger;
public RoslynController(IRoslynBridgeClient bridgeClient, ILogger<RoslynController> logger)
{
_bridgeClient = bridgeClient;
_logger = logger;
}
/// <summary>
/// Execute a Roslyn query
/// </summary>
/// <param name="request">The query request</param>
/// <param name="instancePort">Optional: specific VS instance port to target</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>The query result</returns>
/// <response code="200">Query executed successfully</response>
/// <response code="400">Invalid request</response>
/// <response code="500">Internal server error</response>
[HttpPost("query")]
[ProducesResponseType(typeof(RoslynQueryResponse), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<RoslynQueryResponse>> 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);
}
/// <summary>
/// Get all projects in the solution
/// </summary>
/// <param name="instancePort">Optional: specific VS instance port to target</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>List of projects</returns>
[HttpGet("projects")]
[ProducesResponseType(typeof(RoslynQueryResponse), StatusCodes.Status200OK)]
public async Task<ActionResult<RoslynQueryResponse>> 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);
}
/// <summary>
/// Get solution overview
/// </summary>
/// <param name="instancePort">Optional: specific VS instance port to target</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>Solution statistics and overview</returns>
[HttpGet("solution/overview")]
[ProducesResponseType(typeof(RoslynQueryResponse), StatusCodes.Status200OK)]
public async Task<ActionResult<RoslynQueryResponse>> 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);
}
/// <summary>
/// Get diagnostics (errors and warnings)
/// </summary>
/// <param name="filePath">Optional file path to filter diagnostics</param>
/// <param name="instancePort">Optional: specific VS instance port to target</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>List of diagnostics</returns>
[HttpGet("diagnostics")]
[ProducesResponseType(typeof(RoslynQueryResponse), StatusCodes.Status200OK)]
public async Task<ActionResult<RoslynQueryResponse>> 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);
}
/// <summary>
/// Get symbol information at a specific position
/// </summary>
/// <param name="filePath">File path</param>
/// <param name="line">Line number (1-based)</param>
/// <param name="column">Column number (0-based)</param>
/// <param name="instancePort">Optional: specific VS instance port to target</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>Symbol information</returns>
[HttpGet("symbol")]
[ProducesResponseType(typeof(RoslynQueryResponse), StatusCodes.Status200OK)]
public async Task<ActionResult<RoslynQueryResponse>> 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);
}
/// <summary>
/// Find all references to a symbol
/// </summary>
/// <param name="filePath">File path</param>
/// <param name="line">Line number (1-based)</param>
/// <param name="column">Column number (0-based)</param>
/// <param name="instancePort">Optional: specific VS instance port to target</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>List of references</returns>
[HttpGet("references")]
[ProducesResponseType(typeof(RoslynQueryResponse), StatusCodes.Status200OK)]
public async Task<ActionResult<RoslynQueryResponse>> 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);
}
/// <summary>
/// Search for symbols by name
/// </summary>
/// <param name="symbolName">Symbol name or pattern</param>
/// <param name="kind">Optional symbol kind filter</param>
/// <param name="instancePort">Optional: specific VS instance port to target</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>List of matching symbols</returns>
[HttpGet("symbol/search")]
[ProducesResponseType(typeof(RoslynQueryResponse), StatusCodes.Status200OK)]
public async Task<ActionResult<RoslynQueryResponse>> 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<string, string> { ["kind"] = kind };
}
var result = await _bridgeClient.ExecuteQueryAsync(request, instancePort, cancellationToken);
return Ok(result);
}
/// <summary>
/// Format a document
/// </summary>
/// <param name="filePath">File path to format</param>
/// <param name="instancePort">Optional: specific VS instance port to target</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>Format operation result</returns>
[HttpPost("format")]
[ProducesResponseType(typeof(RoslynQueryResponse), StatusCodes.Status200OK)]
public async Task<ActionResult<RoslynQueryResponse>> 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);
}
/// <summary>
/// Add a NuGet package to a project
/// </summary>
/// <param name="projectName">Project name</param>
/// <param name="packageName">NuGet package name</param>
/// <param name="version">Optional package version</param>
/// <param name="instancePort">Optional: specific VS instance port to target</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>Operation result</returns>
[HttpPost("project/package/add")]
[ProducesResponseType(typeof(RoslynQueryResponse), StatusCodes.Status200OK)]
public async Task<ActionResult<RoslynQueryResponse>> 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);
}
/// <summary>
/// Build a project
/// </summary>
/// <param name="projectName">Project name</param>
/// <param name="configuration">Build configuration (Debug/Release)</param>
/// <param name="instancePort">Optional: specific VS instance port to target</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>Build result</returns>
[HttpPost("project/build")]
[ProducesResponseType(typeof(RoslynQueryResponse), StatusCodes.Status200OK)]
public async Task<ActionResult<RoslynQueryResponse>> 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);
}
}

View File

@@ -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;
/// <summary>
/// Middleware to capture and log all Roslyn API requests and responses
/// </summary>
public class HistoryMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<HistoryMiddleware> _logger;
public HistoryMiddleware(RequestDelegate next, ILogger<HistoryMiddleware> 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<RoslynQueryRequest>(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<RoslynQueryResponse>(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");
}
}
}
}
/// <summary>
/// Extension methods for adding history middleware
/// </summary>
public static class HistoryMiddlewareExtensions
{
public static IApplicationBuilder UseHistoryTracking(this IApplicationBuilder builder)
{
return builder.UseMiddleware<HistoryMiddleware>();
}
}

View File

@@ -0,0 +1,32 @@
namespace RoslynBridge.WebApi.Models;
/// <summary>
/// Health check response for service status
/// </summary>
public class HealthCheckResponse
{
/// <summary>
/// Overall health status
/// </summary>
public string Status { get; set; } = "Healthy";
/// <summary>
/// Web API service status
/// </summary>
public string WebApiStatus { get; set; } = "Running";
/// <summary>
/// Visual Studio plugin connection status
/// </summary>
public string VsPluginStatus { get; set; } = "Unknown";
/// <summary>
/// Timestamp of the health check
/// </summary>
public DateTime Timestamp { get; set; } = DateTime.UtcNow;
/// <summary>
/// API version
/// </summary>
public string Version { get; set; } = "1.0.0";
}

View File

@@ -0,0 +1,52 @@
namespace RoslynBridge.WebApi.Models;
/// <summary>
/// Represents a single query history entry with request and response information
/// </summary>
public class QueryHistoryEntry
{
/// <summary>
/// Unique identifier for this history entry
/// </summary>
public string Id { get; set; } = Guid.NewGuid().ToString();
/// <summary>
/// Timestamp when the request was received
/// </summary>
public DateTime Timestamp { get; set; } = DateTime.UtcNow;
/// <summary>
/// The endpoint path that was called
/// </summary>
public string Path { get; set; } = string.Empty;
/// <summary>
/// HTTP method used
/// </summary>
public string Method { get; set; } = string.Empty;
/// <summary>
/// The Roslyn query request
/// </summary>
public RoslynQueryRequest? Request { get; set; }
/// <summary>
/// The Roslyn query response
/// </summary>
public RoslynQueryResponse? Response { get; set; }
/// <summary>
/// Request duration in milliseconds
/// </summary>
public long DurationMs { get; set; }
/// <summary>
/// Whether the request was successful
/// </summary>
public bool Success { get; set; }
/// <summary>
/// Client IP address
/// </summary>
public string? ClientIp { get; set; }
}

View File

@@ -0,0 +1,65 @@
using System.ComponentModel.DataAnnotations;
namespace RoslynBridge.WebApi.Models;
/// <summary>
/// Request model for Roslyn query operations
/// </summary>
public class RoslynQueryRequest
{
/// <summary>
/// Type of query to execute (e.g., "getsymbol", "getdocument", "findreferences")
/// </summary>
[Required]
public string QueryType { get; set; } = string.Empty;
/// <summary>
/// File path for file-based operations
/// </summary>
public string? FilePath { get; set; }
/// <summary>
/// Symbol name for symbol-based operations
/// </summary>
public string? SymbolName { get; set; }
/// <summary>
/// Line number (1-based) for position-based operations
/// </summary>
public int? Line { get; set; }
/// <summary>
/// Column number (0-based) for position-based operations
/// </summary>
public int? Column { get; set; }
/// <summary>
/// Additional parameters for the query
/// </summary>
public Dictionary<string, string>? Parameters { get; set; }
/// <summary>
/// Project name for project operations
/// </summary>
public string? ProjectName { get; set; }
/// <summary>
/// Package name for NuGet operations
/// </summary>
public string? PackageName { get; set; }
/// <summary>
/// Version for NuGet package operations
/// </summary>
public string? Version { get; set; }
/// <summary>
/// Build configuration (Debug/Release)
/// </summary>
public string? Configuration { get; set; }
/// <summary>
/// Directory path for directory operations
/// </summary>
public string? DirectoryPath { get; set; }
}

View File

@@ -0,0 +1,27 @@
namespace RoslynBridge.WebApi.Models;
/// <summary>
/// Response model for Roslyn query operations
/// </summary>
public class RoslynQueryResponse
{
/// <summary>
/// Indicates whether the operation was successful
/// </summary>
public bool Success { get; set; }
/// <summary>
/// Optional message providing additional context
/// </summary>
public string? Message { get; set; }
/// <summary>
/// The response data (structure varies by query type)
/// </summary>
public object? Data { get; set; }
/// <summary>
/// Error message if the operation failed
/// </summary>
public string? Error { get; set; }
}

View File

@@ -0,0 +1,42 @@
namespace RoslynBridge.WebApi.Models;
/// <summary>
/// Information about a registered Visual Studio instance
/// </summary>
public class VSInstanceInfo
{
/// <summary>
/// The port number where this VS instance is listening
/// </summary>
public int Port { get; set; }
/// <summary>
/// The process ID of the Visual Studio instance
/// </summary>
public int ProcessId { get; set; }
/// <summary>
/// The solution file path (if any solution is open)
/// </summary>
public string? SolutionPath { get; set; }
/// <summary>
/// The solution name (if any solution is open)
/// </summary>
public string? SolutionName { get; set; }
/// <summary>
/// When this instance was registered
/// </summary>
public DateTime RegisteredAt { get; set; }
/// <summary>
/// Last heartbeat time
/// </summary>
public DateTime LastHeartbeat { get; set; }
/// <summary>
/// List of project names in the solution
/// </summary>
public List<string> Projects { get; set; } = new();
}

View File

@@ -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<IHistoryService, HistoryService>();
// Register instance registry service as singleton
builder.Services.AddSingleton<IInstanceRegistryService, InstanceRegistryService>();
// Register background service for cleaning up stale instances
builder.Services.AddHostedService<InstanceCleanupService>();
// Configure HttpClient for Roslyn Bridge
var roslynBridgeUrl = builder.Configuration.GetValue<string>("RoslynBridge:BaseUrl") ?? "http://localhost:59123";
builder.Services.AddHttpClient<IRoslynBridgeClient, RoslynBridgeClient>(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<ILogger<Program>>();
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();

View File

@@ -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"
}
}
}
}

View File

@@ -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<RoslynQueryResponse>();
```
## 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

View File

@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>$(NoWarn);1591</NoWarn>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="9.0.10" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
</ItemGroup>
</Project>

View File

@@ -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.

View File

@@ -0,0 +1,59 @@
using System.Collections.Concurrent;
using RoslynBridge.WebApi.Models;
namespace RoslynBridge.WebApi.Services;
/// <summary>
/// In-memory implementation of history service
/// </summary>
public class HistoryService : IHistoryService
{
private readonly ConcurrentQueue<QueryHistoryEntry> _entries = new();
private readonly ILogger<HistoryService> _logger;
private readonly int _maxEntries;
public HistoryService(ILogger<HistoryService> logger, IConfiguration configuration)
{
_logger = logger;
_maxEntries = configuration.GetValue<int>("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<QueryHistoryEntry> GetAll()
{
return _entries.Reverse();
}
public QueryHistoryEntry? GetById(string id)
{
return _entries.FirstOrDefault(e => e.Id == id);
}
public IEnumerable<QueryHistoryEntry> GetRecent(int count = 50)
{
return _entries.Reverse().Take(count);
}
public void Clear()
{
_entries.Clear();
_logger.LogInformation("History cleared");
}
public int GetCount()
{
return _entries.Count;
}
}

View File

@@ -0,0 +1,46 @@
using RoslynBridge.WebApi.Models;
namespace RoslynBridge.WebApi.Services;
/// <summary>
/// Service for managing query history
/// </summary>
public interface IHistoryService
{
/// <summary>
/// Add a new history entry
/// </summary>
/// <param name="entry">The history entry to add</param>
void Add(QueryHistoryEntry entry);
/// <summary>
/// Get all history entries
/// </summary>
/// <returns>List of all history entries</returns>
IEnumerable<QueryHistoryEntry> GetAll();
/// <summary>
/// Get a specific history entry by ID
/// </summary>
/// <param name="id">The entry ID</param>
/// <returns>The history entry, or null if not found</returns>
QueryHistoryEntry? GetById(string id);
/// <summary>
/// Get recent history entries
/// </summary>
/// <param name="count">Number of entries to return</param>
/// <returns>List of recent history entries</returns>
IEnumerable<QueryHistoryEntry> GetRecent(int count = 50);
/// <summary>
/// Clear all history entries
/// </summary>
void Clear();
/// <summary>
/// Get total count of history entries
/// </summary>
/// <returns>Total number of entries</returns>
int GetCount();
}

View File

@@ -0,0 +1,49 @@
using RoslynBridge.WebApi.Models;
namespace RoslynBridge.WebApi.Services;
/// <summary>
/// Service for managing registered Visual Studio instances
/// </summary>
public interface IInstanceRegistryService
{
/// <summary>
/// Register a new Visual Studio instance
/// </summary>
void Register(VSInstanceInfo instance);
/// <summary>
/// Unregister a Visual Studio instance by process ID
/// </summary>
bool Unregister(int processId);
/// <summary>
/// Update heartbeat for an instance
/// </summary>
bool UpdateHeartbeat(int processId);
/// <summary>
/// Get all registered instances
/// </summary>
IEnumerable<VSInstanceInfo> GetAllInstances();
/// <summary>
/// Get instance by process ID
/// </summary>
VSInstanceInfo? GetByProcessId(int processId);
/// <summary>
/// Get instance by solution path
/// </summary>
VSInstanceInfo? GetBySolutionPath(string solutionPath);
/// <summary>
/// Get instance by port
/// </summary>
VSInstanceInfo? GetByPort(int port);
/// <summary>
/// Remove stale instances (no heartbeat for specified timeout)
/// </summary>
void RemoveStaleInstances(TimeSpan timeout);
}

View File

@@ -0,0 +1,26 @@
using RoslynBridge.WebApi.Models;
namespace RoslynBridge.WebApi.Services;
/// <summary>
/// Interface for communicating with the Roslyn Bridge Visual Studio plugin
/// </summary>
public interface IRoslynBridgeClient
{
/// <summary>
/// Execute a query against the Roslyn Bridge server
/// </summary>
/// <param name="request">The query request</param>
/// <param name="instancePort">Optional port of specific VS instance to target</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>The query response</returns>
Task<RoslynQueryResponse> ExecuteQueryAsync(RoslynQueryRequest request, int? instancePort = null, CancellationToken cancellationToken = default);
/// <summary>
/// Check if the Roslyn Bridge server is healthy
/// </summary>
/// <param name="instancePort">Optional port of specific VS instance to check</param>
/// <param name="cancellationToken">Cancellation token</param>
/// <returns>True if healthy, false otherwise</returns>
Task<bool> IsHealthyAsync(int? instancePort = null, CancellationToken cancellationToken = default);
}

View File

@@ -0,0 +1,50 @@
namespace RoslynBridge.WebApi.Services;
/// <summary>
/// Background service that periodically removes stale VS instances
/// </summary>
public class InstanceCleanupService : BackgroundService
{
private readonly IServiceProvider _serviceProvider;
private readonly ILogger<InstanceCleanupService> _logger;
private readonly TimeSpan _cleanupInterval = TimeSpan.FromMinutes(1);
private readonly TimeSpan _staleTimeout = TimeSpan.FromMinutes(5);
public InstanceCleanupService(
IServiceProvider serviceProvider,
ILogger<InstanceCleanupService> 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<IInstanceRegistryService>();
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");
}
}

View File

@@ -0,0 +1,115 @@
using System.Collections.Concurrent;
using RoslynBridge.WebApi.Models;
namespace RoslynBridge.WebApi.Services;
/// <summary>
/// Thread-safe in-memory registry for Visual Studio instances
/// </summary>
public class InstanceRegistryService : IInstanceRegistryService
{
private readonly ConcurrentDictionary<int, VSInstanceInfo> _instances = new();
private readonly ILogger<InstanceRegistryService> _logger;
public InstanceRegistryService(ILogger<InstanceRegistryService> 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<VSInstanceInfo> 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);
}
}
}
}

View File

@@ -0,0 +1,174 @@
using System.Text;
using System.Text.Json;
using RoslynBridge.WebApi.Models;
namespace RoslynBridge.WebApi.Services;
/// <summary>
/// HTTP client for communicating with the Roslyn Bridge Visual Studio plugin
/// </summary>
public class RoslynBridgeClient : IRoslynBridgeClient
{
private readonly HttpClient _httpClient;
private readonly IInstanceRegistryService _registryService;
private readonly ILogger<RoslynBridgeClient> _logger;
private readonly JsonSerializerOptions _jsonOptions;
public RoslynBridgeClient(
HttpClient httpClient,
IInstanceRegistryService registryService,
ILogger<RoslynBridgeClient> logger)
{
_httpClient = httpClient;
_registryService = registryService;
_logger = logger;
_jsonOptions = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
}
public async Task<RoslynQueryResponse> 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<RoslynQueryResponse>(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<bool> 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;
}
}
/// <summary>
/// Resolves which VS instance port to use based on provided hints
/// </summary>
private Task<int?> ResolveInstancePortAsync(int? explicitPort, RoslynQueryRequest? request)
{
// If explicit port specified, use it
if (explicitPort.HasValue)
{
return Task.FromResult<int?>(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<int?>(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<int?>(instances[0].Port);
}
_logger.LogWarning("No Visual Studio instances registered");
return Task.FromResult<int?>(null);
}
}

View File

@@ -0,0 +1,12 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"RoslynBridge.WebApi": "Debug"
}
},
"RoslynBridge": {
"BaseUrl": "http://localhost:59123"
}
}

View File

@@ -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"
}
}
}
}

View File

@@ -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
}

Binary file not shown.

View File

@@ -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"
}
}
}

Binary file not shown.

View File

@@ -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
}
}
}

View File

@@ -0,0 +1 @@
{"Version":1,"ManifestType":"Publish","Endpoints":[]}

View File

@@ -0,0 +1,576 @@
<?xml version="1.0"?>
<doc>
<assembly>
<name>RoslynBridge.WebApi</name>
</assembly>
<members>
<member name="T:RoslynBridge.WebApi.Controllers.HealthController">
<summary>
Health check and status controller
</summary>
</member>
<member name="M:RoslynBridge.WebApi.Controllers.HealthController.GetHealth(System.Threading.CancellationToken)">
<summary>
Get the health status of the middleware and Visual Studio plugin
</summary>
<param name="cancellationToken">Cancellation token</param>
<returns>Health status information</returns>
<response code="200">Service is healthy</response>
<response code="503">Service is unhealthy</response>
</member>
<member name="M:RoslynBridge.WebApi.Controllers.HealthController.Ping">
<summary>
Simple ping endpoint
</summary>
<returns>Pong response</returns>
</member>
<member name="T:RoslynBridge.WebApi.Controllers.HistoryController">
<summary>
Controller for accessing query history
</summary>
</member>
<member name="M:RoslynBridge.WebApi.Controllers.HistoryController.GetAll">
<summary>
Get all history entries
</summary>
<returns>List of all history entries</returns>
<response code="200">Returns the list of history entries</response>
</member>
<member name="M:RoslynBridge.WebApi.Controllers.HistoryController.GetById(System.String)">
<summary>
Get a specific history entry by ID
</summary>
<param name="id">The history entry ID</param>
<returns>The history entry</returns>
<response code="200">Returns the history entry</response>
<response code="404">Entry not found</response>
</member>
<member name="M:RoslynBridge.WebApi.Controllers.HistoryController.GetRecent(System.Int32)">
<summary>
Get recent history entries
</summary>
<param name="count">Number of entries to return (default: 50, max: 500)</param>
<returns>List of recent history entries</returns>
<response code="200">Returns the list of recent entries</response>
</member>
<member name="M:RoslynBridge.WebApi.Controllers.HistoryController.GetStats">
<summary>
Get history statistics
</summary>
<returns>Statistics about history entries</returns>
</member>
<member name="M:RoslynBridge.WebApi.Controllers.HistoryController.Clear">
<summary>
Clear all history entries
</summary>
<returns>Confirmation message</returns>
<response code="200">History cleared successfully</response>
</member>
<member name="T:RoslynBridge.WebApi.Controllers.InstancesController">
<summary>
Controller for managing Visual Studio instance registrations
</summary>
</member>
<member name="M:RoslynBridge.WebApi.Controllers.InstancesController.Register(RoslynBridge.WebApi.Models.VSInstanceInfo)">
<summary>
Register a new Visual Studio instance
</summary>
<param name="instance">Instance information</param>
<returns>Registration result</returns>
</member>
<member name="M:RoslynBridge.WebApi.Controllers.InstancesController.Unregister(System.Int32)">
<summary>
Unregister a Visual Studio instance
</summary>
<param name="processId">Process ID of the instance to unregister</param>
<returns>Unregistration result</returns>
</member>
<member name="M:RoslynBridge.WebApi.Controllers.InstancesController.Heartbeat(System.Int32)">
<summary>
Update heartbeat for a Visual Studio instance
</summary>
<param name="processId">Process ID of the instance</param>
<returns>Heartbeat update result</returns>
</member>
<member name="M:RoslynBridge.WebApi.Controllers.InstancesController.GetAll">
<summary>
Get all registered Visual Studio instances
</summary>
<returns>List of registered instances</returns>
</member>
<member name="M:RoslynBridge.WebApi.Controllers.InstancesController.GetByProcessId(System.Int32)">
<summary>
Get instance by process ID
</summary>
<param name="processId">Process ID</param>
<returns>Instance information</returns>
</member>
<member name="M:RoslynBridge.WebApi.Controllers.InstancesController.GetBySolutionPath(System.String)">
<summary>
Get instance by solution path
</summary>
<param name="solutionPath">Solution file path</param>
<returns>Instance information</returns>
</member>
<member name="M:RoslynBridge.WebApi.Controllers.InstancesController.GetByPort(System.Int32)">
<summary>
Get instance by port
</summary>
<param name="port">Port number</param>
<returns>Instance information</returns>
</member>
<member name="T:RoslynBridge.WebApi.Controllers.RoslynController">
<summary>
Controller for Roslyn code analysis operations
</summary>
</member>
<member name="M:RoslynBridge.WebApi.Controllers.RoslynController.ExecuteQuery(RoslynBridge.WebApi.Models.RoslynQueryRequest,System.Nullable{System.Int32},System.Threading.CancellationToken)">
<summary>
Execute a Roslyn query
</summary>
<param name="request">The query request</param>
<param name="instancePort">Optional: specific VS instance port to target</param>
<param name="cancellationToken">Cancellation token</param>
<returns>The query result</returns>
<response code="200">Query executed successfully</response>
<response code="400">Invalid request</response>
<response code="500">Internal server error</response>
</member>
<member name="M:RoslynBridge.WebApi.Controllers.RoslynController.GetProjects(System.Nullable{System.Int32},System.Threading.CancellationToken)">
<summary>
Get all projects in the solution
</summary>
<param name="instancePort">Optional: specific VS instance port to target</param>
<param name="cancellationToken">Cancellation token</param>
<returns>List of projects</returns>
</member>
<member name="M:RoslynBridge.WebApi.Controllers.RoslynController.GetSolutionOverview(System.Nullable{System.Int32},System.Threading.CancellationToken)">
<summary>
Get solution overview
</summary>
<param name="instancePort">Optional: specific VS instance port to target</param>
<param name="cancellationToken">Cancellation token</param>
<returns>Solution statistics and overview</returns>
</member>
<member name="M:RoslynBridge.WebApi.Controllers.RoslynController.GetDiagnostics(System.String,System.Nullable{System.Int32},System.Threading.CancellationToken)">
<summary>
Get diagnostics (errors and warnings)
</summary>
<param name="filePath">Optional file path to filter diagnostics</param>
<param name="instancePort">Optional: specific VS instance port to target</param>
<param name="cancellationToken">Cancellation token</param>
<returns>List of diagnostics</returns>
</member>
<member name="M:RoslynBridge.WebApi.Controllers.RoslynController.GetSymbol(System.String,System.Int32,System.Int32,System.Nullable{System.Int32},System.Threading.CancellationToken)">
<summary>
Get symbol information at a specific position
</summary>
<param name="filePath">File path</param>
<param name="line">Line number (1-based)</param>
<param name="column">Column number (0-based)</param>
<param name="instancePort">Optional: specific VS instance port to target</param>
<param name="cancellationToken">Cancellation token</param>
<returns>Symbol information</returns>
</member>
<member name="M:RoslynBridge.WebApi.Controllers.RoslynController.FindReferences(System.String,System.Int32,System.Int32,System.Nullable{System.Int32},System.Threading.CancellationToken)">
<summary>
Find all references to a symbol
</summary>
<param name="filePath">File path</param>
<param name="line">Line number (1-based)</param>
<param name="column">Column number (0-based)</param>
<param name="instancePort">Optional: specific VS instance port to target</param>
<param name="cancellationToken">Cancellation token</param>
<returns>List of references</returns>
</member>
<member name="M:RoslynBridge.WebApi.Controllers.RoslynController.FindSymbol(System.String,System.String,System.Nullable{System.Int32},System.Threading.CancellationToken)">
<summary>
Search for symbols by name
</summary>
<param name="symbolName">Symbol name or pattern</param>
<param name="kind">Optional symbol kind filter</param>
<param name="instancePort">Optional: specific VS instance port to target</param>
<param name="cancellationToken">Cancellation token</param>
<returns>List of matching symbols</returns>
</member>
<member name="M:RoslynBridge.WebApi.Controllers.RoslynController.FormatDocument(System.String,System.Nullable{System.Int32},System.Threading.CancellationToken)">
<summary>
Format a document
</summary>
<param name="filePath">File path to format</param>
<param name="instancePort">Optional: specific VS instance port to target</param>
<param name="cancellationToken">Cancellation token</param>
<returns>Format operation result</returns>
</member>
<member name="M:RoslynBridge.WebApi.Controllers.RoslynController.AddNuGetPackage(System.String,System.String,System.String,System.Nullable{System.Int32},System.Threading.CancellationToken)">
<summary>
Add a NuGet package to a project
</summary>
<param name="projectName">Project name</param>
<param name="packageName">NuGet package name</param>
<param name="version">Optional package version</param>
<param name="instancePort">Optional: specific VS instance port to target</param>
<param name="cancellationToken">Cancellation token</param>
<returns>Operation result</returns>
</member>
<member name="M:RoslynBridge.WebApi.Controllers.RoslynController.BuildProject(System.String,System.String,System.Nullable{System.Int32},System.Threading.CancellationToken)">
<summary>
Build a project
</summary>
<param name="projectName">Project name</param>
<param name="configuration">Build configuration (Debug/Release)</param>
<param name="instancePort">Optional: specific VS instance port to target</param>
<param name="cancellationToken">Cancellation token</param>
<returns>Build result</returns>
</member>
<member name="T:RoslynBridge.WebApi.Middleware.HistoryMiddleware">
<summary>
Middleware to capture and log all Roslyn API requests and responses
</summary>
</member>
<member name="T:RoslynBridge.WebApi.Middleware.HistoryMiddlewareExtensions">
<summary>
Extension methods for adding history middleware
</summary>
</member>
<member name="T:RoslynBridge.WebApi.Models.HealthCheckResponse">
<summary>
Health check response for service status
</summary>
</member>
<member name="P:RoslynBridge.WebApi.Models.HealthCheckResponse.Status">
<summary>
Overall health status
</summary>
</member>
<member name="P:RoslynBridge.WebApi.Models.HealthCheckResponse.WebApiStatus">
<summary>
Web API service status
</summary>
</member>
<member name="P:RoslynBridge.WebApi.Models.HealthCheckResponse.VsPluginStatus">
<summary>
Visual Studio plugin connection status
</summary>
</member>
<member name="P:RoslynBridge.WebApi.Models.HealthCheckResponse.Timestamp">
<summary>
Timestamp of the health check
</summary>
</member>
<member name="P:RoslynBridge.WebApi.Models.HealthCheckResponse.Version">
<summary>
API version
</summary>
</member>
<member name="T:RoslynBridge.WebApi.Models.QueryHistoryEntry">
<summary>
Represents a single query history entry with request and response information
</summary>
</member>
<member name="P:RoslynBridge.WebApi.Models.QueryHistoryEntry.Id">
<summary>
Unique identifier for this history entry
</summary>
</member>
<member name="P:RoslynBridge.WebApi.Models.QueryHistoryEntry.Timestamp">
<summary>
Timestamp when the request was received
</summary>
</member>
<member name="P:RoslynBridge.WebApi.Models.QueryHistoryEntry.Path">
<summary>
The endpoint path that was called
</summary>
</member>
<member name="P:RoslynBridge.WebApi.Models.QueryHistoryEntry.Method">
<summary>
HTTP method used
</summary>
</member>
<member name="P:RoslynBridge.WebApi.Models.QueryHistoryEntry.Request">
<summary>
The Roslyn query request
</summary>
</member>
<member name="P:RoslynBridge.WebApi.Models.QueryHistoryEntry.Response">
<summary>
The Roslyn query response
</summary>
</member>
<member name="P:RoslynBridge.WebApi.Models.QueryHistoryEntry.DurationMs">
<summary>
Request duration in milliseconds
</summary>
</member>
<member name="P:RoslynBridge.WebApi.Models.QueryHistoryEntry.Success">
<summary>
Whether the request was successful
</summary>
</member>
<member name="P:RoslynBridge.WebApi.Models.QueryHistoryEntry.ClientIp">
<summary>
Client IP address
</summary>
</member>
<member name="T:RoslynBridge.WebApi.Models.RoslynQueryRequest">
<summary>
Request model for Roslyn query operations
</summary>
</member>
<member name="P:RoslynBridge.WebApi.Models.RoslynQueryRequest.QueryType">
<summary>
Type of query to execute (e.g., "getsymbol", "getdocument", "findreferences")
</summary>
</member>
<member name="P:RoslynBridge.WebApi.Models.RoslynQueryRequest.FilePath">
<summary>
File path for file-based operations
</summary>
</member>
<member name="P:RoslynBridge.WebApi.Models.RoslynQueryRequest.SymbolName">
<summary>
Symbol name for symbol-based operations
</summary>
</member>
<member name="P:RoslynBridge.WebApi.Models.RoslynQueryRequest.Line">
<summary>
Line number (1-based) for position-based operations
</summary>
</member>
<member name="P:RoslynBridge.WebApi.Models.RoslynQueryRequest.Column">
<summary>
Column number (0-based) for position-based operations
</summary>
</member>
<member name="P:RoslynBridge.WebApi.Models.RoslynQueryRequest.Parameters">
<summary>
Additional parameters for the query
</summary>
</member>
<member name="P:RoslynBridge.WebApi.Models.RoslynQueryRequest.ProjectName">
<summary>
Project name for project operations
</summary>
</member>
<member name="P:RoslynBridge.WebApi.Models.RoslynQueryRequest.PackageName">
<summary>
Package name for NuGet operations
</summary>
</member>
<member name="P:RoslynBridge.WebApi.Models.RoslynQueryRequest.Version">
<summary>
Version for NuGet package operations
</summary>
</member>
<member name="P:RoslynBridge.WebApi.Models.RoslynQueryRequest.Configuration">
<summary>
Build configuration (Debug/Release)
</summary>
</member>
<member name="P:RoslynBridge.WebApi.Models.RoslynQueryRequest.DirectoryPath">
<summary>
Directory path for directory operations
</summary>
</member>
<member name="T:RoslynBridge.WebApi.Models.RoslynQueryResponse">
<summary>
Response model for Roslyn query operations
</summary>
</member>
<member name="P:RoslynBridge.WebApi.Models.RoslynQueryResponse.Success">
<summary>
Indicates whether the operation was successful
</summary>
</member>
<member name="P:RoslynBridge.WebApi.Models.RoslynQueryResponse.Message">
<summary>
Optional message providing additional context
</summary>
</member>
<member name="P:RoslynBridge.WebApi.Models.RoslynQueryResponse.Data">
<summary>
The response data (structure varies by query type)
</summary>
</member>
<member name="P:RoslynBridge.WebApi.Models.RoslynQueryResponse.Error">
<summary>
Error message if the operation failed
</summary>
</member>
<member name="T:RoslynBridge.WebApi.Models.VSInstanceInfo">
<summary>
Information about a registered Visual Studio instance
</summary>
</member>
<member name="P:RoslynBridge.WebApi.Models.VSInstanceInfo.Port">
<summary>
The port number where this VS instance is listening
</summary>
</member>
<member name="P:RoslynBridge.WebApi.Models.VSInstanceInfo.ProcessId">
<summary>
The process ID of the Visual Studio instance
</summary>
</member>
<member name="P:RoslynBridge.WebApi.Models.VSInstanceInfo.SolutionPath">
<summary>
The solution file path (if any solution is open)
</summary>
</member>
<member name="P:RoslynBridge.WebApi.Models.VSInstanceInfo.SolutionName">
<summary>
The solution name (if any solution is open)
</summary>
</member>
<member name="P:RoslynBridge.WebApi.Models.VSInstanceInfo.RegisteredAt">
<summary>
When this instance was registered
</summary>
</member>
<member name="P:RoslynBridge.WebApi.Models.VSInstanceInfo.LastHeartbeat">
<summary>
Last heartbeat time
</summary>
</member>
<member name="P:RoslynBridge.WebApi.Models.VSInstanceInfo.Projects">
<summary>
List of project names in the solution
</summary>
</member>
<member name="T:RoslynBridge.WebApi.Services.HistoryService">
<summary>
In-memory implementation of history service
</summary>
</member>
<member name="T:RoslynBridge.WebApi.Services.IHistoryService">
<summary>
Service for managing query history
</summary>
</member>
<member name="M:RoslynBridge.WebApi.Services.IHistoryService.Add(RoslynBridge.WebApi.Models.QueryHistoryEntry)">
<summary>
Add a new history entry
</summary>
<param name="entry">The history entry to add</param>
</member>
<member name="M:RoslynBridge.WebApi.Services.IHistoryService.GetAll">
<summary>
Get all history entries
</summary>
<returns>List of all history entries</returns>
</member>
<member name="M:RoslynBridge.WebApi.Services.IHistoryService.GetById(System.String)">
<summary>
Get a specific history entry by ID
</summary>
<param name="id">The entry ID</param>
<returns>The history entry, or null if not found</returns>
</member>
<member name="M:RoslynBridge.WebApi.Services.IHistoryService.GetRecent(System.Int32)">
<summary>
Get recent history entries
</summary>
<param name="count">Number of entries to return</param>
<returns>List of recent history entries</returns>
</member>
<member name="M:RoslynBridge.WebApi.Services.IHistoryService.Clear">
<summary>
Clear all history entries
</summary>
</member>
<member name="M:RoslynBridge.WebApi.Services.IHistoryService.GetCount">
<summary>
Get total count of history entries
</summary>
<returns>Total number of entries</returns>
</member>
<member name="T:RoslynBridge.WebApi.Services.IInstanceRegistryService">
<summary>
Service for managing registered Visual Studio instances
</summary>
</member>
<member name="M:RoslynBridge.WebApi.Services.IInstanceRegistryService.Register(RoslynBridge.WebApi.Models.VSInstanceInfo)">
<summary>
Register a new Visual Studio instance
</summary>
</member>
<member name="M:RoslynBridge.WebApi.Services.IInstanceRegistryService.Unregister(System.Int32)">
<summary>
Unregister a Visual Studio instance by process ID
</summary>
</member>
<member name="M:RoslynBridge.WebApi.Services.IInstanceRegistryService.UpdateHeartbeat(System.Int32)">
<summary>
Update heartbeat for an instance
</summary>
</member>
<member name="M:RoslynBridge.WebApi.Services.IInstanceRegistryService.GetAllInstances">
<summary>
Get all registered instances
</summary>
</member>
<member name="M:RoslynBridge.WebApi.Services.IInstanceRegistryService.GetByProcessId(System.Int32)">
<summary>
Get instance by process ID
</summary>
</member>
<member name="M:RoslynBridge.WebApi.Services.IInstanceRegistryService.GetBySolutionPath(System.String)">
<summary>
Get instance by solution path
</summary>
</member>
<member name="M:RoslynBridge.WebApi.Services.IInstanceRegistryService.GetByPort(System.Int32)">
<summary>
Get instance by port
</summary>
</member>
<member name="M:RoslynBridge.WebApi.Services.IInstanceRegistryService.RemoveStaleInstances(System.TimeSpan)">
<summary>
Remove stale instances (no heartbeat for specified timeout)
</summary>
</member>
<member name="T:RoslynBridge.WebApi.Services.InstanceCleanupService">
<summary>
Background service that periodically removes stale VS instances
</summary>
</member>
<member name="T:RoslynBridge.WebApi.Services.InstanceRegistryService">
<summary>
Thread-safe in-memory registry for Visual Studio instances
</summary>
</member>
<member name="T:RoslynBridge.WebApi.Services.IRoslynBridgeClient">
<summary>
Interface for communicating with the Roslyn Bridge Visual Studio plugin
</summary>
</member>
<member name="M:RoslynBridge.WebApi.Services.IRoslynBridgeClient.ExecuteQueryAsync(RoslynBridge.WebApi.Models.RoslynQueryRequest,System.Nullable{System.Int32},System.Threading.CancellationToken)">
<summary>
Execute a query against the Roslyn Bridge server
</summary>
<param name="request">The query request</param>
<param name="instancePort">Optional port of specific VS instance to target</param>
<param name="cancellationToken">Cancellation token</param>
<returns>The query response</returns>
</member>
<member name="M:RoslynBridge.WebApi.Services.IRoslynBridgeClient.IsHealthyAsync(System.Nullable{System.Int32},System.Threading.CancellationToken)">
<summary>
Check if the Roslyn Bridge server is healthy
</summary>
<param name="instancePort">Optional port of specific VS instance to check</param>
<param name="cancellationToken">Cancellation token</param>
<returns>True if healthy, false otherwise</returns>
</member>
<member name="T:RoslynBridge.WebApi.Services.RoslynBridgeClient">
<summary>
HTTP client for communicating with the Roslyn Bridge Visual Studio plugin
</summary>
</member>
<member name="M:RoslynBridge.WebApi.Services.RoslynBridgeClient.ResolveInstancePortAsync(System.Nullable{System.Int32},RoslynBridge.WebApi.Models.RoslynQueryRequest)">
<summary>
Resolves which VS instance port to use based on provided hints
</summary>
</member>
</members>
</doc>

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,12 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"RoslynBridge.WebApi": "Debug"
}
},
"RoslynBridge": {
"BaseUrl": "http://localhost:59123"
}
}

View File

@@ -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"
}
}
}
}

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<location path="." inheritInChildApplications="false">
<system.webServer>
<handlers>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" />
</handlers>
<aspNetCore processPath="dotnet" arguments=".\RoslynBridge.WebApi.dll" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" hostingModel="inprocess" />
</system.webServer>
</location>
</configuration>
<!--ProjectGuid: F257A158-E5B4-569A-2B64-6EA9157F9C52-->

View File

@@ -4,6 +4,8 @@ VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RoslynBridge", "RoslynBridge\RoslynBridge.csproj", "{B2C3D4E5-F6A7-4B5C-9D8E-0F1A2B3C4D5E}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RoslynBridge", "RoslynBridge\RoslynBridge.csproj", "{B2C3D4E5-F6A7-4B5C-9D8E-0F1A2B3C4D5E}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RoslynBridge.WebApi", "RoslynBridge.WebApi\RoslynBridge.WebApi.csproj", "{F257A158-E5B4-569A-2B64-6EA9157F9C52}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU 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}.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.ActiveCfg = Release|Any CPU
{B2C3D4E5-F6A7-4B5C-9D8E-0F1A2B3C4D5E}.Release|Any CPU.Build.0 = 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 EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE