Compare commits
10 Commits
0054386700
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| a7d4f352b7 | |||
| 86500af544 | |||
| daa60c53dd | |||
| cd0f96ca1e | |||
| cade9dcc47 | |||
| 89a349ae72 | |||
| 1cbfba3893 | |||
| 716827a665 | |||
| 1ad0a98d4d | |||
| 48d85e1906 |
@@ -20,7 +20,21 @@
|
||||
"Bash(\"C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\MSBuild\\Current\\Bin\\MSBuild.exe\" /t:Restore,Build /p:Configuration=Debug)",
|
||||
"Bash(\"C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\MSBuild\\Current\\Bin\\MSBuild.exe\" RoslynBridge.sln /t:Restore /t:Build /p:Configuration=Debug)",
|
||||
"Bash(del \".claude\\init.md\")",
|
||||
"Bash(git add:*)"
|
||||
"Bash(git add:*)",
|
||||
"Bash(dotnet --version:*)",
|
||||
"Bash(dotnet new:*)",
|
||||
"Bash(del \"C:\\Users\\AJ\\Desktop\\RoslynBridge\\RoslynBridge.WebApi\\Controllers\\WeatherForecastController.cs\")",
|
||||
"Bash(del \"C:\\Users\\AJ\\Desktop\\RoslynBridge\\RoslynBridge.WebApi\\WeatherForecast.cs\")",
|
||||
"Bash(dotnet run:*)",
|
||||
"Bash(\"C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\MSBuild\\Current\\Bin\\MSBuild.exe\" \"C:\\Users\\AJ\\Desktop\\RoslynBridge\\RoslynBridge\\RoslynBridge.csproj\" /t:Restore,Build /p:Configuration=Debug)",
|
||||
"Bash(\"C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\MSBuild\\Current\\Bin\\MSBuild.exe\" RoslynBridge.csproj /t:Build /p:Configuration=Debug)",
|
||||
"Read(//c/Users/AJ/.claude/skills/roslyn-bridge/**)",
|
||||
"Bash(dotnet add:*)",
|
||||
"Bash(dotnet publish:*)",
|
||||
"Read(//c/Users/AJ/AppData/Local/Microsoft/VisualStudio/**)",
|
||||
"Bash(powershell -ExecutionPolicy Bypass -File install.ps1 -SkipBuild -SkipPublish)",
|
||||
"Bash(\"C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\MSBuild\\Current\\Bin\\MSBuild.exe\" RoslynBridgeRoslynBridge.csproj /t:Restore,Build /p:Configuration=Debug)",
|
||||
"Bash(git commit:*)"
|
||||
],
|
||||
"deny": [],
|
||||
"ask": []
|
||||
|
||||
@@ -3,73 +3,122 @@ name: roslyn-bridge
|
||||
description: Use this for C# code analysis, querying .NET projects, finding symbols, getting diagnostics, or any Roslyn/semantic analysis tasks using the bridge server
|
||||
---
|
||||
|
||||
# Roslyn Bridge Testing Guide
|
||||
# Roslyn Bridge API Guide
|
||||
|
||||
Use this guide when testing or accessing the Claude Roslyn Bridge HTTP endpoints.
|
||||
Use this guide when accessing the Roslyn Bridge for C# code analysis.
|
||||
|
||||
## Server Info
|
||||
- **Base URL**: `http://localhost:59123`
|
||||
- **Endpoints**:
|
||||
- `/query` - Main query endpoint for all Roslyn operations
|
||||
- `/health` - Health check endpoint to verify server is running
|
||||
- **Method**: POST
|
||||
## Architecture
|
||||
|
||||
```
|
||||
┌─────────────┐ REST API ┌────────────────────┐ HTTP ┌─────────────────────┐
|
||||
│ Claude │ ◄─────────────────► │ Web API (:5001) │ ◄────────────► │ VS Plugin (:59123) │
|
||||
│ AI │ │ Middleware │ │ Roslyn Bridge │
|
||||
└─────────────┘ └────────────────────┘ └─────────────────────┘
|
||||
```
|
||||
|
||||
## Recommended: Web API (Port 5001)
|
||||
|
||||
**Base URL**: `http://localhost:5001`
|
||||
|
||||
The Web API provides a modern RESTful interface with:
|
||||
- Clean REST endpoints with query parameters
|
||||
- Full Swagger documentation at `/`
|
||||
- Request/response history tracking at `/api/history`
|
||||
- CORS support for web applications
|
||||
- Better error handling and logging
|
||||
|
||||
### Web API Endpoints
|
||||
|
||||
**Health & Info:**
|
||||
- `GET /api/health` - Check health of both Web API and VS plugin
|
||||
- `GET /api/health/ping` - Simple ping
|
||||
|
||||
**Roslyn Operations:**
|
||||
- `GET /api/roslyn/projects` - Get all projects
|
||||
- `GET /api/roslyn/solution/overview` - Solution statistics
|
||||
- `GET /api/roslyn/diagnostics?filePath={path}` - Get errors/warnings
|
||||
- `GET /api/roslyn/symbol?filePath={path}&line={line}&column={col}` - Get symbol info
|
||||
- `GET /api/roslyn/references?filePath={path}&line={line}&column={col}` - Find references
|
||||
- `GET /api/roslyn/symbol/search?symbolName={name}&kind={kind}` - Search symbols
|
||||
- `POST /api/roslyn/query` - Execute any query (fallback to raw queries)
|
||||
- `POST /api/roslyn/format` - Format document
|
||||
- `POST /api/roslyn/project/package/add` - Add NuGet package
|
||||
- `POST /api/roslyn/project/build` - Build project
|
||||
|
||||
**History:**
|
||||
- `GET /api/history` - All history entries
|
||||
- `GET /api/history/{id}` - Specific entry
|
||||
- `GET /api/history/recent?count=50` - Recent entries
|
||||
- `GET /api/history/stats` - Statistics
|
||||
- `DELETE /api/history` - Clear history
|
||||
|
||||
## Alternative: Direct VS Plugin Access (Port 59123)
|
||||
|
||||
**Base URL**: `http://localhost:59123`
|
||||
|
||||
Direct access to the Visual Studio plugin (use only if Web API is unavailable):
|
||||
- **Endpoints**: `/query`, `/health`
|
||||
- **Method**: POST only
|
||||
- **Content-Type**: application/json
|
||||
|
||||
## ⚠️ CRITICAL: Command Syntax Rules
|
||||
|
||||
**ALWAYS use curl with the Bash tool. NEVER pipe curl output to PowerShell.**
|
||||
|
||||
### ✅ CORRECT: Using curl with Bash tool
|
||||
### ✅ CORRECT: Using Web API (Recommended)
|
||||
|
||||
```bash
|
||||
# Test if server is running
|
||||
curl -X POST http://localhost:59123/query -H "Content-Type: application/json" -d "{\"queryType\":\"getprojects\"}"
|
||||
# Health check
|
||||
curl http://localhost:5001/api/health
|
||||
|
||||
# Get diagnostics
|
||||
curl -X POST http://localhost:59123/query -H "Content-Type: application/json" -d "{\"queryType\":\"getdiagnostics\"}"
|
||||
# Get all projects (returns full file paths)
|
||||
curl http://localhost:5001/api/roslyn/projects
|
||||
|
||||
# Get document info
|
||||
curl -X POST http://localhost:59123/query -H "Content-Type: application/json" -d "{\"queryType\":\"getdocument\",\"filePath\":\"C:\\\\path\\\\to\\\\file.cs\"}"
|
||||
# Get solution overview
|
||||
curl http://localhost:5001/api/roslyn/solution/overview
|
||||
|
||||
# Get symbol at position
|
||||
curl -X POST http://localhost:59123/query -H "Content-Type: application/json" -d "{\"queryType\":\"getsymbol\",\"filePath\":\"C:\\\\Users\\\\AJ\\\\Desktop\\\\PepLib\\\\PepLib\\\\Program.cs\",\"line\":10,\"column\":5}"
|
||||
# Get diagnostics for entire solution
|
||||
curl http://localhost:5001/api/roslyn/diagnostics
|
||||
|
||||
# Get diagnostics for specific file
|
||||
curl "http://localhost:5001/api/roslyn/diagnostics?filePath=C:/path/to/file.cs"
|
||||
|
||||
# Get symbol at position (use paths from /projects response)
|
||||
curl "http://localhost:5001/api/roslyn/symbol?filePath=C:/path/to/file.cs&line=10&column=5"
|
||||
|
||||
# Find all references
|
||||
curl "http://localhost:5001/api/roslyn/references?filePath=C:/path/to/file.cs&line=18&column=30"
|
||||
|
||||
# Search for symbols
|
||||
curl "http://localhost:5001/api/roslyn/symbol/search?symbolName=MyClass&kind=class"
|
||||
|
||||
# Get recent history
|
||||
curl http://localhost:5001/api/history/recent?count=10
|
||||
|
||||
# Get history statistics
|
||||
curl http://localhost:5001/api/history/stats
|
||||
```
|
||||
|
||||
**IMPORTANT curl syntax rules:**
|
||||
- Use `-X POST` (NOT `-Method POST`)
|
||||
- Use `-H` for headers (NOT `-Headers`)
|
||||
- Use `-d` for data (NOT `-Body`)
|
||||
- Escape backslashes in file paths: `\\\\` becomes `\\` in JSON
|
||||
- For GET requests, no `-X GET` needed
|
||||
- Use quotes around URLs with query parameters
|
||||
- Forward slashes `/` work in file paths (Windows accepts both `/` and `\`)
|
||||
- **DO NOT pipe to PowerShell** - the JSON output is already formatted
|
||||
|
||||
### ❌ WRONG: DO NOT DO THIS
|
||||
### Using Direct VS Plugin Access (Fallback)
|
||||
|
||||
```bash
|
||||
# NEVER pipe curl to PowerShell - this will fail on Windows
|
||||
curl ... | powershell -Command "$json = $input | ..." # ❌ WRONG
|
||||
# Health check - verify VS plugin is running
|
||||
curl -X POST http://localhost:59123/health -H "Content-Type: application/json" -d "{}"
|
||||
|
||||
# Get projects via POST
|
||||
curl -X POST http://localhost:59123/query -H "Content-Type: application/json" -d "{\"queryType\":\"getprojects\"}"
|
||||
|
||||
# Get symbol at position
|
||||
curl -X POST http://localhost:59123/query -H "Content-Type: application/json" -d "{\"queryType\":\"getsymbol\",\"filePath\":\"C:\\\\path\\\\to\\\\file.cs\",\"line\":10,\"column\":5}"
|
||||
```
|
||||
|
||||
### Using PowerShell (Alternative)
|
||||
|
||||
Run these commands **directly in PowerShell**, NOT via `powershell -Command`:
|
||||
|
||||
```powershell
|
||||
# Test server
|
||||
$body = @{queryType='getprojects'} | ConvertTo-Json
|
||||
Invoke-RestMethod -Uri 'http://localhost:59123/query' -Method Post -Body $body -ContentType 'application/json'
|
||||
|
||||
# Get document
|
||||
$body = @{
|
||||
queryType='getdocument'
|
||||
filePath='C:\path\to\file.cs'
|
||||
} | ConvertTo-Json
|
||||
Invoke-RestMethod -Uri 'http://localhost:59123/query' -Method Post -Body $body -ContentType 'application/json'
|
||||
```
|
||||
|
||||
**IMPORTANT PowerShell rules:**
|
||||
- Run directly in PowerShell console, NOT via `bash -c` or `powershell -Command`
|
||||
- Use single quotes around URI and content type
|
||||
- File paths don't need escaping in PowerShell hash tables
|
||||
**Note:** In POST requests to VS plugin, escape backslashes: `\\\\` becomes `\\` in JSON
|
||||
|
||||
## Quick Reference: All Endpoints
|
||||
|
||||
@@ -115,82 +164,104 @@ Invoke-RestMethod -Uri 'http://localhost:59123/query' -Method Post -Body $body -
|
||||
| `restorepackages` | `projectName` | - | Restore NuGet packages |
|
||||
| `createdirectory` | `directoryPath` | - | Create a new directory |
|
||||
|
||||
## Common Test Examples
|
||||
## Common Workflow Examples
|
||||
|
||||
### Typical Code Analysis Workflow
|
||||
|
||||
```bash
|
||||
# Health check - verify server is running
|
||||
curl -X POST http://localhost:59123/health -H "Content-Type: application/json" -d "{}"
|
||||
# Step 1: Check if services are healthy
|
||||
curl http://localhost:5001/api/health
|
||||
|
||||
# Get all projects in solution
|
||||
curl -X POST http://localhost:59123/query -H "Content-Type: application/json" -d "{\"queryType\":\"getprojects\"}"
|
||||
# Step 2: Get all projects and their files
|
||||
curl http://localhost:5001/api/roslyn/projects
|
||||
# Response includes: {"data": [{"documents": ["C:/Full/Path/To/File.cs", ...]}]}
|
||||
|
||||
# Get solution overview
|
||||
curl -X POST http://localhost:59123/query -H "Content-Type: application/json" -d "{\"queryType\":\"getsolutionoverview\"}"
|
||||
# Step 3: Use the full paths from Step 2 in subsequent queries
|
||||
FILE="C:/Users/AJ/Desktop/MyProject/Program.cs"
|
||||
|
||||
# Get diagnostics for entire solution
|
||||
curl -X POST http://localhost:59123/query -H "Content-Type: application/json" -d "{\"queryType\":\"getdiagnostics\"}"
|
||||
# Get diagnostics (errors/warnings) for a file
|
||||
curl "http://localhost:5001/api/roslyn/diagnostics?filePath=$FILE"
|
||||
|
||||
# Get semantic model for a file
|
||||
curl -X POST http://localhost:59123/query -H "Content-Type: application/json" -d "{\"queryType\":\"getsemanticmodel\",\"filePath\":\"C:\\\\path\\\\to\\\\file.cs\"}"
|
||||
# Get symbol information at a specific location
|
||||
curl "http://localhost:5001/api/roslyn/symbol?filePath=$FILE&line=15&column=10"
|
||||
|
||||
# Get syntax tree for a file
|
||||
curl -X POST http://localhost:59123/query -H "Content-Type: application/json" -d "{\"queryType\":\"getsyntaxtree\",\"filePath\":\"C:\\\\path\\\\to\\\\file.cs\"}"
|
||||
# Find all references to that symbol
|
||||
curl "http://localhost:5001/api/roslyn/references?filePath=$FILE&line=15&column=10"
|
||||
|
||||
# Find all classes containing "Helper"
|
||||
curl -X POST http://localhost:59123/query -H "Content-Type: application/json" -d "{\"queryType\":\"searchcode\",\"symbolName\":\".*Helper\",\"parameters\":{\"scope\":\"classes\"}}"
|
||||
# Step 4: View your query history
|
||||
curl http://localhost:5001/api/history/recent?count=5
|
||||
|
||||
# Format a document
|
||||
curl -X POST http://localhost:59123/query -H "Content-Type: application/json" -d "{\"queryType\":\"formatdocument\",\"filePath\":\"C:\\\\path\\\\to\\\\file.cs\"}"
|
||||
# Step 5: Get statistics about your queries
|
||||
curl http://localhost:5001/api/history/stats
|
||||
```
|
||||
|
||||
### Advanced Query Examples
|
||||
|
||||
```bash
|
||||
# Search for all classes with "Service" in the name
|
||||
curl "http://localhost:5001/api/roslyn/symbol/search?symbolName=Service&kind=class"
|
||||
|
||||
# Get solution-wide statistics
|
||||
curl http://localhost:5001/api/roslyn/solution/overview
|
||||
|
||||
# For complex queries not available as REST endpoints, use POST /query
|
||||
curl -X POST http://localhost:5001/api/roslyn/query \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"queryType":"searchcode","symbolName":".*Controller","parameters":{"scope":"classes"}}'
|
||||
|
||||
# Build a project
|
||||
curl -X POST "http://localhost:5001/api/roslyn/project/build?projectName=MyProject"
|
||||
|
||||
# Add NuGet package
|
||||
curl -X POST http://localhost:59123/query -H "Content-Type: application/json" -d "{\"queryType\":\"addnugetpackage\",\"projectName\":\"RoslynBridge\",\"packageName\":\"Newtonsoft.Json\"}"
|
||||
|
||||
# Add NuGet package with version
|
||||
curl -X POST http://localhost:59123/query -H "Content-Type: application/json" -d "{\"queryType\":\"addnugetpackage\",\"projectName\":\"RoslynBridge\",\"packageName\":\"Newtonsoft.Json\",\"version\":\"13.0.3\"}"
|
||||
|
||||
# Remove NuGet package
|
||||
curl -X POST http://localhost:59123/query -H "Content-Type: application/json" -d "{\"queryType\":\"removenugetpackage\",\"projectName\":\"RoslynBridge\",\"packageName\":\"Newtonsoft.Json\"}"
|
||||
|
||||
# Build project
|
||||
curl -X POST http://localhost:59123/query -H "Content-Type: application/json" -d "{\"queryType\":\"buildproject\",\"projectName\":\"RoslynBridge\"}"
|
||||
|
||||
# Build with configuration
|
||||
curl -X POST http://localhost:59123/query -H "Content-Type: application/json" -d "{\"queryType\":\"buildproject\",\"projectName\":\"RoslynBridge\",\"configuration\":\"Release\"}"
|
||||
|
||||
# Clean project
|
||||
curl -X POST http://localhost:59123/query -H "Content-Type: application/json" -d "{\"queryType\":\"cleanproject\",\"projectName\":\"RoslynBridge\"}"
|
||||
|
||||
# Restore packages
|
||||
curl -X POST http://localhost:59123/query -H "Content-Type: application/json" -d "{\"queryType\":\"restorepackages\",\"projectName\":\"RoslynBridge\"}"
|
||||
|
||||
# Create directory
|
||||
curl -X POST http://localhost:59123/query -H "Content-Type: application/json" -d "{\"queryType\":\"createdirectory\",\"directoryPath\":\"C:\\\\path\\\\to\\\\new\\\\directory\"}"
|
||||
curl -X POST "http://localhost:5001/api/roslyn/project/package/add?projectName=MyProject&packageName=Newtonsoft.Json&version=13.0.3"
|
||||
```
|
||||
|
||||
## Response Format
|
||||
|
||||
Success:
|
||||
All endpoints return JSON in this format:
|
||||
|
||||
**Success:**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "Optional message",
|
||||
"data": { /* Response data */ }
|
||||
"data": { /* Response data varies by endpoint */ },
|
||||
"error": null
|
||||
}
|
||||
```
|
||||
|
||||
Error:
|
||||
**Error:**
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"error": "Error message",
|
||||
"data": null
|
||||
"message": null,
|
||||
"data": null,
|
||||
"error": "Error description"
|
||||
}
|
||||
```
|
||||
|
||||
## Notes
|
||||
## Important Notes
|
||||
|
||||
- Line numbers are **1-based**
|
||||
- Column numbers are **0-based**
|
||||
- File paths in JSON need escaped backslashes: `C:\\path\\to\\file.cs`
|
||||
- All workspace modifications use VS threading model (`JoinableTaskFactory.SwitchToMainThreadAsync()`)
|
||||
- The Visual Studio extension must be running for endpoints to work
|
||||
- Line numbers are **1-based** (first line is 1)
|
||||
- Column numbers are **0-based** (first column is 0)
|
||||
- **Use full paths from `/api/roslyn/projects` response** in other endpoints
|
||||
- Forward slashes `/` work in file paths (Windows accepts both `/` and `\`)
|
||||
- Both the Web API (port 5000) and VS Plugin (port 59123) must be running
|
||||
- All workspace modifications use VS threading model
|
||||
- Request history is automatically tracked in the Web API
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**"Failed to connect" error:**
|
||||
1. Check if Web API is running: `curl http://localhost:5001/api/health/ping`
|
||||
2. Check if VS Plugin is running: `curl -X POST http://localhost:59123/health -H "Content-Type: application/json" -d "{}"`
|
||||
3. Ensure Visual Studio is open with a solution loaded
|
||||
|
||||
**"vsPluginStatus": "Disconnected":**
|
||||
- The VS Plugin (port 59123) is not running
|
||||
- Open Visual Studio and ensure the RoslynBridge extension is loaded
|
||||
|
||||
**Empty or null data:**
|
||||
- Ensure you're using the correct file paths from `/api/roslyn/projects`
|
||||
- Verify line/column numbers are within the file bounds
|
||||
- Check that the file is part of the currently open VS solution
|
||||
|
||||
53
AGENTS.md
53
AGENTS.md
@@ -37,3 +37,56 @@
|
||||
- Server binds to `http://localhost:59123/` and accepts only POST; CORS is permissive for local tooling. Do not expose externally.
|
||||
- Endpoints: `/query` (main), `/health` route exists but still requires POST.
|
||||
- Adjust port/paths in `RoslynBridge/Constants/ServerConstants.cs` if needed.
|
||||
|
||||
## Agent Script Usage: rb.ps1 (for AI agents)
|
||||
|
||||
This section is for the agent only. Use `rb.ps1` to minimize tokens and speed up local Roslyn queries.
|
||||
|
||||
- Location: `rb.ps1` (repo root). Invoke with PowerShell.
|
||||
- Auto-detects endpoints: prefers Web API (`http://localhost:5001`), falls back to VS plugin (`http://localhost:59123`).
|
||||
- Output defaults to compact JSON. Use `-Raw` to emit raw response strings, `-Pretty` only for debugging.
|
||||
- Override detection with `-BaseUrl http://localhost:5001` if needed; `-NoDetect` forces Web API pathing.
|
||||
|
||||
Examples (run from repo root):
|
||||
|
||||
```powershell
|
||||
# Health and discovery
|
||||
powershell -NoProfile -Command .\rb.ps1 health
|
||||
powershell -NoProfile -Command .\rb.ps1 projects -Raw
|
||||
powershell -NoProfile -Command .\rb.ps1 overview
|
||||
|
||||
# File-specific queries (use full paths from `projects`)
|
||||
powershell -NoProfile -Command .\rb.ps1 diagnostics -FilePath C:/full/path/File.cs
|
||||
powershell -NoProfile -Command .\rb.ps1 symbol -FilePath C:/full/path/File.cs -Line 12 -Column 4
|
||||
powershell -NoProfile -Command .\rb.ps1 references -FilePath C:/full/path/File.cs -Line 12 -Column 4 -Raw
|
||||
|
||||
# Symbol search
|
||||
powershell -NoProfile -Command .\rb.ps1 search -SymbolName Service -Kind class
|
||||
|
||||
# Project operations
|
||||
powershell -NoProfile -Command .\rb.ps1 build -ProjectName MyProject
|
||||
powershell -NoProfile -Command .\rb.ps1 package-add -ProjectName MyProject -PackageName Newtonsoft.Json -Version 13.0.3
|
||||
|
||||
# History (Web API only)
|
||||
powershell -NoProfile -Command .\rb.ps1 history-recent -Count 10
|
||||
powershell -NoProfile -Command .\rb.ps1 history-stats
|
||||
|
||||
# Compact solution overview summary (uses API-level summary)
|
||||
powershell -NoProfile -Command .\rb.ps1 solution-overview -Format yaml -Top 5
|
||||
|
||||
```
|
||||
|
||||
Key behaviors and assumptions:
|
||||
- Lines are 1-based; columns 0-based.
|
||||
- Forward slashes `/` are accepted in file paths.
|
||||
- Prefer `-Raw` to minimize token usage when chaining outputs.
|
||||
- If Web API is down, commands transparently fall back to the plugin when possible. History endpoints require Web API.
|
||||
- If auto-detection is ambiguous, set `-BaseUrl` explicitly.
|
||||
|
||||
Policy (agent-only):
|
||||
- Do NOT call `curl`, `Invoke-RestMethod`, or `Invoke-WebRequest` directly to Roslyn Bridge.
|
||||
- Always invoke `rb.ps1` for all Roslyn-related queries and operations.
|
||||
- Prefer `-Raw` output and parse only what’s needed to reduce tokens.
|
||||
- Keep timeouts short unless debugging (`-TimeoutSec` can be raised temporarily).
|
||||
- Avoid pretty output unless explicitly needed (`-Pretty`).
|
||||
|
||||
|
||||
72
RoslynBridge.WebApi/Controllers/HealthController.cs
Normal file
72
RoslynBridge.WebApi/Controllers/HealthController.cs
Normal 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 });
|
||||
}
|
||||
}
|
||||
117
RoslynBridge.WebApi/Controllers/HistoryController.cs
Normal file
117
RoslynBridge.WebApi/Controllers/HistoryController.cs
Normal 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" });
|
||||
}
|
||||
}
|
||||
168
RoslynBridge.WebApi/Controllers/InstancesController.cs
Normal file
168
RoslynBridge.WebApi/Controllers/InstancesController.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
410
RoslynBridge.WebApi/Controllers/RoslynController.cs
Normal file
410
RoslynBridge.WebApi/Controllers/RoslynController.cs
Normal file
@@ -0,0 +1,410 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using RoslynBridge.WebApi.Models;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Linq;
|
||||
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 a compact summary of the solution overview
|
||||
/// </summary>
|
||||
/// <param name="topNProjects">How many projects to list (by file count)</param>
|
||||
/// <param name="includeNamespaces">Include top-level namespaces in summary</param>
|
||||
/// <param name="format">One of: json | text | yaml</param>
|
||||
/// <param name="instancePort">Optional: specific VS instance port to target</param>
|
||||
/// <param name="cancellationToken">Cancellation token</param>
|
||||
/// <returns>Compact summary as JSON or plain text</returns>
|
||||
[HttpGet("solution/overview/summary")]
|
||||
[ProducesResponseType(typeof(RoslynQueryResponse), StatusCodes.Status200OK)]
|
||||
public async Task<IActionResult> GetSolutionOverviewSummary(
|
||||
[FromQuery] int topNProjects = 5,
|
||||
[FromQuery] bool includeNamespaces = true,
|
||||
[FromQuery] string? format = "json",
|
||||
[FromQuery] int? instancePort = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var request = new RoslynQueryRequest { QueryType = "getsolutionoverview" };
|
||||
var result = await _bridgeClient.ExecuteQueryAsync(request, instancePort, cancellationToken);
|
||||
|
||||
if (!result.Success)
|
||||
{
|
||||
if (string.Equals(format, "json", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return Ok(result);
|
||||
}
|
||||
var err = result.Error ?? "Unknown error";
|
||||
return Content($"error: {err}", "text/plain", Encoding.UTF8);
|
||||
}
|
||||
|
||||
// Expect Data as JsonElement; shape per Models.SolutionOverview
|
||||
int projectCount = 0;
|
||||
int documentCount = 0;
|
||||
var projectsSummary = new List<(string Name, int FileCount)>();
|
||||
var topLevelNamespaces = new List<string>();
|
||||
|
||||
if (result.Data is JsonElement dataEl && dataEl.ValueKind == JsonValueKind.Object)
|
||||
{
|
||||
if (dataEl.TryGetProperty("projectCount", out var pc)) projectCount = pc.GetInt32();
|
||||
if (dataEl.TryGetProperty("documentCount", out var dc)) documentCount = dc.GetInt32();
|
||||
if (includeNamespaces && dataEl.TryGetProperty("topLevelNamespaces", out var nsEl) && nsEl.ValueKind == JsonValueKind.Array)
|
||||
{
|
||||
foreach (var ns in nsEl.EnumerateArray())
|
||||
{
|
||||
if (ns.ValueKind == JsonValueKind.String)
|
||||
{
|
||||
topLevelNamespaces.Add(ns.GetString() ?? string.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dataEl.TryGetProperty("projects", out var prEl) && prEl.ValueKind == JsonValueKind.Array)
|
||||
{
|
||||
foreach (var p in prEl.EnumerateArray())
|
||||
{
|
||||
string name = p.TryGetProperty("name", out var nEl) && nEl.ValueKind == JsonValueKind.String ? (nEl.GetString() ?? string.Empty) : string.Empty;
|
||||
int files = p.TryGetProperty("fileCount", out var fEl) && fEl.ValueKind == JsonValueKind.Number ? fEl.GetInt32() : 0;
|
||||
projectsSummary.Add((name, files));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var topProjects = projectsSummary
|
||||
.OrderByDescending(p => p.FileCount)
|
||||
.ThenBy(p => p.Name)
|
||||
.Take(Math.Max(0, topNProjects))
|
||||
.ToList();
|
||||
|
||||
if (string.Equals(format, "json", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var compact = new
|
||||
{
|
||||
projectCount,
|
||||
documentCount,
|
||||
projects = topProjects.Select(p => new { name = p.Name, files = p.FileCount }).ToList(),
|
||||
topLevelNamespaces = includeNamespaces ? topLevelNamespaces : null
|
||||
};
|
||||
|
||||
return Ok(new RoslynQueryResponse
|
||||
{
|
||||
Success = true,
|
||||
Message = "Compact solution overview",
|
||||
Data = compact,
|
||||
Error = null
|
||||
});
|
||||
}
|
||||
|
||||
// text or yaml: render a compact string
|
||||
var sb = new StringBuilder();
|
||||
if (string.Equals(format, "yaml", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
sb.AppendLine($"projectCount: {projectCount}");
|
||||
sb.AppendLine($"documentCount: {documentCount}");
|
||||
if (includeNamespaces)
|
||||
{
|
||||
sb.Append("topLevelNamespaces: [");
|
||||
sb.Append(string.Join(", ", topLevelNamespaces));
|
||||
sb.AppendLine("]");
|
||||
}
|
||||
sb.AppendLine("projects:");
|
||||
foreach (var p in topProjects)
|
||||
{
|
||||
sb.AppendLine($" - name: {p.Name}");
|
||||
sb.AppendLine($" files: {p.FileCount}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// text
|
||||
sb.AppendLine($"Projects: {projectCount} | Documents: {documentCount}");
|
||||
if (includeNamespaces && topLevelNamespaces.Count > 0)
|
||||
{
|
||||
sb.AppendLine($"TopNamespaces: {string.Join(", ", topLevelNamespaces)}");
|
||||
}
|
||||
sb.AppendLine("Top Projects:");
|
||||
foreach (var p in topProjects)
|
||||
{
|
||||
sb.AppendLine($" - {p.Name} ({p.FileCount})");
|
||||
}
|
||||
}
|
||||
|
||||
return Content(sb.ToString(), "text/plain", Encoding.UTF8);
|
||||
}
|
||||
|
||||
/// <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);
|
||||
}
|
||||
}
|
||||
132
RoslynBridge.WebApi/Middleware/HistoryMiddleware.cs
Normal file
132
RoslynBridge.WebApi/Middleware/HistoryMiddleware.cs
Normal 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>();
|
||||
}
|
||||
}
|
||||
32
RoslynBridge.WebApi/Models/HealthCheckResponse.cs
Normal file
32
RoslynBridge.WebApi/Models/HealthCheckResponse.cs
Normal 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";
|
||||
}
|
||||
52
RoslynBridge.WebApi/Models/QueryHistoryEntry.cs
Normal file
52
RoslynBridge.WebApi/Models/QueryHistoryEntry.cs
Normal 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; }
|
||||
}
|
||||
65
RoslynBridge.WebApi/Models/RoslynQueryRequest.cs
Normal file
65
RoslynBridge.WebApi/Models/RoslynQueryRequest.cs
Normal 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; }
|
||||
}
|
||||
27
RoslynBridge.WebApi/Models/RoslynQueryResponse.cs
Normal file
27
RoslynBridge.WebApi/Models/RoslynQueryResponse.cs
Normal 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; }
|
||||
}
|
||||
42
RoslynBridge.WebApi/Models/VSInstanceInfo.cs
Normal file
42
RoslynBridge.WebApi/Models/VSInstanceInfo.cs
Normal 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();
|
||||
}
|
||||
129
RoslynBridge.WebApi/Program.cs
Normal file
129
RoslynBridge.WebApi/Program.cs
Normal 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();
|
||||
38
RoslynBridge.WebApi/Properties/launchSettings.json
Normal file
38
RoslynBridge.WebApi/Properties/launchSettings.json
Normal 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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
378
RoslynBridge.WebApi/README.md
Normal file
378
RoslynBridge.WebApi/README.md
Normal 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
|
||||
17
RoslynBridge.WebApi/RoslynBridge.WebApi.csproj
Normal file
17
RoslynBridge.WebApi/RoslynBridge.WebApi.csproj
Normal 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>
|
||||
351
RoslynBridge.WebApi/SERVICE_SETUP.md
Normal file
351
RoslynBridge.WebApi/SERVICE_SETUP.md
Normal 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.
|
||||
59
RoslynBridge.WebApi/Services/HistoryService.cs
Normal file
59
RoslynBridge.WebApi/Services/HistoryService.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
46
RoslynBridge.WebApi/Services/IHistoryService.cs
Normal file
46
RoslynBridge.WebApi/Services/IHistoryService.cs
Normal 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();
|
||||
}
|
||||
49
RoslynBridge.WebApi/Services/IInstanceRegistryService.cs
Normal file
49
RoslynBridge.WebApi/Services/IInstanceRegistryService.cs
Normal 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);
|
||||
}
|
||||
26
RoslynBridge.WebApi/Services/IRoslynBridgeClient.cs
Normal file
26
RoslynBridge.WebApi/Services/IRoslynBridgeClient.cs
Normal 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);
|
||||
}
|
||||
50
RoslynBridge.WebApi/Services/InstanceCleanupService.cs
Normal file
50
RoslynBridge.WebApi/Services/InstanceCleanupService.cs
Normal 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");
|
||||
}
|
||||
}
|
||||
115
RoslynBridge.WebApi/Services/InstanceRegistryService.cs
Normal file
115
RoslynBridge.WebApi/Services/InstanceRegistryService.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
174
RoslynBridge.WebApi/Services/RoslynBridgeClient.cs
Normal file
174
RoslynBridge.WebApi/Services/RoslynBridgeClient.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
12
RoslynBridge.WebApi/appsettings.Development.json
Normal file
12
RoslynBridge.WebApi/appsettings.Development.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning",
|
||||
"RoslynBridge.WebApi": "Debug"
|
||||
}
|
||||
},
|
||||
"RoslynBridge": {
|
||||
"BaseUrl": "http://localhost:59123"
|
||||
}
|
||||
}
|
||||
33
RoslynBridge.WebApi/appsettings.json
Normal file
33
RoslynBridge.WebApi/appsettings.json
Normal 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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
361
RoslynBridge.WebApi/install.ps1
Normal file
361
RoslynBridge.WebApi/install.ps1
Normal 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.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
RoslynBridge.WebApi/publish/Microsoft.Extensions.Diagnostics.dll
Normal file
BIN
RoslynBridge.WebApi/publish/Microsoft.Extensions.Diagnostics.dll
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
RoslynBridge.WebApi/publish/Microsoft.Extensions.Hosting.dll
Normal file
BIN
RoslynBridge.WebApi/publish/Microsoft.Extensions.Hosting.dll
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
RoslynBridge.WebApi/publish/Microsoft.Extensions.Logging.dll
Normal file
BIN
RoslynBridge.WebApi/publish/Microsoft.Extensions.Logging.dll
Normal file
Binary file not shown.
Binary file not shown.
BIN
RoslynBridge.WebApi/publish/Microsoft.Extensions.Options.dll
Normal file
BIN
RoslynBridge.WebApi/publish/Microsoft.Extensions.Options.dll
Normal file
Binary file not shown.
BIN
RoslynBridge.WebApi/publish/Microsoft.Extensions.Primitives.dll
Normal file
BIN
RoslynBridge.WebApi/publish/Microsoft.Extensions.Primitives.dll
Normal file
Binary file not shown.
BIN
RoslynBridge.WebApi/publish/Microsoft.OpenApi.dll
Normal file
BIN
RoslynBridge.WebApi/publish/Microsoft.OpenApi.dll
Normal file
Binary file not shown.
834
RoslynBridge.WebApi/publish/RoslynBridge.WebApi.deps.json
Normal file
834
RoslynBridge.WebApi/publish/RoslynBridge.WebApi.deps.json
Normal 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"
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
RoslynBridge.WebApi/publish/RoslynBridge.WebApi.dll
Normal file
BIN
RoslynBridge.WebApi/publish/RoslynBridge.WebApi.dll
Normal file
Binary file not shown.
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"Version": 1,
|
||||
"ManifestType": "Publish",
|
||||
"Endpoints": []
|
||||
}
|
||||
576
RoslynBridge.WebApi/publish/RoslynBridge.WebApi.xml
Normal file
576
RoslynBridge.WebApi/publish/RoslynBridge.WebApi.xml
Normal 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>
|
||||
BIN
RoslynBridge.WebApi/publish/Swashbuckle.AspNetCore.Swagger.dll
Normal file
BIN
RoslynBridge.WebApi/publish/Swashbuckle.AspNetCore.Swagger.dll
Normal file
Binary file not shown.
Binary file not shown.
BIN
RoslynBridge.WebApi/publish/Swashbuckle.AspNetCore.SwaggerUI.dll
Normal file
BIN
RoslynBridge.WebApi/publish/Swashbuckle.AspNetCore.SwaggerUI.dll
Normal file
Binary file not shown.
Binary file not shown.
BIN
RoslynBridge.WebApi/publish/System.Diagnostics.EventLog.dll
Normal file
BIN
RoslynBridge.WebApi/publish/System.Diagnostics.EventLog.dll
Normal file
Binary file not shown.
BIN
RoslynBridge.WebApi/publish/System.IO.Pipelines.dll
Normal file
BIN
RoslynBridge.WebApi/publish/System.IO.Pipelines.dll
Normal file
Binary file not shown.
Binary file not shown.
BIN
RoslynBridge.WebApi/publish/System.Text.Encodings.Web.dll
Normal file
BIN
RoslynBridge.WebApi/publish/System.Text.Encodings.Web.dll
Normal file
Binary file not shown.
BIN
RoslynBridge.WebApi/publish/System.Text.Json.dll
Normal file
BIN
RoslynBridge.WebApi/publish/System.Text.Json.dll
Normal file
Binary file not shown.
12
RoslynBridge.WebApi/publish/appsettings.Development.json
Normal file
12
RoslynBridge.WebApi/publish/appsettings.Development.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning",
|
||||
"RoslynBridge.WebApi": "Debug"
|
||||
}
|
||||
},
|
||||
"RoslynBridge": {
|
||||
"BaseUrl": "http://localhost:59123"
|
||||
}
|
||||
}
|
||||
33
RoslynBridge.WebApi/publish/appsettings.json
Normal file
33
RoslynBridge.WebApi/publish/appsettings.json
Normal 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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
12
RoslynBridge.WebApi/publish/web.config
Normal file
12
RoslynBridge.WebApi/publish/web.config
Normal 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-->
|
||||
@@ -4,6 +4,8 @@ VisualStudioVersion = 17.0.31903.59
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RoslynBridge", "RoslynBridge\RoslynBridge.csproj", "{B2C3D4E5-F6A7-4B5C-9D8E-0F1A2B3C4D5E}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RoslynBridge.WebApi", "RoslynBridge.WebApi\RoslynBridge.WebApi.csproj", "{F257A158-E5B4-569A-2B64-6EA9157F9C52}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -14,6 +16,10 @@ Global
|
||||
{B2C3D4E5-F6A7-4B5C-9D8E-0F1A2B3C4D5E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B2C3D4E5-F6A7-4B5C-9D8E-0F1A2B3C4D5E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B2C3D4E5-F6A7-4B5C-9D8E-0F1A2B3C4D5E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{F257A158-E5B4-569A-2B64-6EA9157F9C52}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F257A158-E5B4-569A-2B64-6EA9157F9C52}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F257A158-E5B4-569A-2B64-6EA9157F9C52}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F257A158-E5B4-569A-2B64-6EA9157F9C52}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
291
RoslynBridge/EXTENSION_MANAGEMENT.md
Normal file
291
RoslynBridge/EXTENSION_MANAGEMENT.md
Normal file
@@ -0,0 +1,291 @@
|
||||
# RoslynBridge Extension Management Guide
|
||||
|
||||
Quick reference for managing the RoslynBridge Visual Studio extension.
|
||||
|
||||
## Common Issues
|
||||
|
||||
### "Extension Already Installed" but Not Visible
|
||||
|
||||
This happens when Visual Studio's extension cache is out of sync. Use the cleanup script:
|
||||
|
||||
```powershell
|
||||
cd C:\Users\AJ\Desktop\RoslynBridge\RoslynBridge
|
||||
.\cleanup-and-reinstall.ps1
|
||||
```
|
||||
|
||||
## Scripts
|
||||
|
||||
### cleanup-and-reinstall.ps1
|
||||
|
||||
Automates extension cleanup and reinstallation.
|
||||
|
||||
**Basic Usage:**
|
||||
|
||||
```powershell
|
||||
# Clean and reinstall everything (default)
|
||||
.\cleanup-and-reinstall.ps1
|
||||
|
||||
# Only clean (remove extension and cache)
|
||||
.\cleanup-and-reinstall.ps1 -Action Clean
|
||||
|
||||
# Only reinstall (skip cleanup)
|
||||
.\cleanup-and-reinstall.ps1 -Action Reinstall
|
||||
|
||||
# Reinstall without rebuilding (use existing VSIX)
|
||||
.\cleanup-and-reinstall.ps1 -Action Reinstall -SkipBuild
|
||||
```
|
||||
|
||||
**What it does:**
|
||||
|
||||
1. **Closes Visual Studio** - Ensures no file locks
|
||||
2. **Cleans Cache** - Removes extension metadata cache
|
||||
3. **Removes Extension** - Deletes installed extension files
|
||||
4. **Rebuilds** - Compiles the extension in Release mode
|
||||
5. **Reinstalls** - Installs the new VSIX
|
||||
6. **Verifies** - Checks installation succeeded
|
||||
|
||||
## Manual Management
|
||||
|
||||
### Check if Extension is Installed
|
||||
|
||||
```powershell
|
||||
# Check extension folder
|
||||
Test-Path "C:\Users\AJ\AppData\Local\Microsoft\VisualStudio\17.0_875bdf7a\Extensions\ulxnn4r3.rql"
|
||||
|
||||
# View extension files
|
||||
ls "C:\Users\AJ\AppData\Local\Microsoft\VisualStudio\17.0_875bdf7a\Extensions\ulxnn4r3.rql"
|
||||
```
|
||||
|
||||
### Manual Cleanup
|
||||
|
||||
```powershell
|
||||
# Close Visual Studio first!
|
||||
|
||||
# Remove cache files
|
||||
Remove-Item "C:\Users\AJ\AppData\Local\Microsoft\VisualStudio\17.0_875bdf7a\Extensions\ExtensionMetadataCache.sqlite" -Force
|
||||
Remove-Item "C:\Users\AJ\AppData\Local\Microsoft\VisualStudio\17.0_875bdf7a\Extensions\ExtensionMetadata.mpack" -Force
|
||||
|
||||
# Remove extension
|
||||
Remove-Item "C:\Users\AJ\AppData\Local\Microsoft\VisualStudio\17.0_875bdf7a\Extensions\ulxnn4r3.rql" -Recurse -Force
|
||||
|
||||
# Remove component cache (optional, full reset)
|
||||
Remove-Item "C:\Users\AJ\AppData\Local\Microsoft\VisualStudio\17.0_875bdf7a\ComponentModelCache" -Recurse -Force
|
||||
```
|
||||
|
||||
### Manual Build
|
||||
|
||||
```powershell
|
||||
cd C:\Users\AJ\Desktop\RoslynBridge\RoslynBridge
|
||||
|
||||
# Build using MSBuild
|
||||
& "C:\Program Files\Microsoft Visual Studio\2022\Community\MSBuild\Current\Bin\MSBuild.exe" `
|
||||
RoslynBridge.csproj `
|
||||
/t:Rebuild `
|
||||
/p:Configuration=Release `
|
||||
/v:minimal
|
||||
```
|
||||
|
||||
### Manual Install
|
||||
|
||||
```powershell
|
||||
# Find the VSIX
|
||||
$vsixPath = (Get-ChildItem -Path . -Filter "*.vsix" -Recurse | Where-Object {
|
||||
$_.FullName -like "*\bin\Release\*"
|
||||
} | Sort-Object LastWriteTime -Descending | Select-Object -First 1).FullName
|
||||
|
||||
# Install using VSIXInstaller
|
||||
& "C:\Program Files\Microsoft Visual Studio\2022\Community\Common7\IDE\VSIXInstaller.exe" `
|
||||
/quiet `
|
||||
/admin `
|
||||
$vsixPath
|
||||
```
|
||||
|
||||
## Testing the Extension
|
||||
|
||||
### 1. Check Visual Studio Extensions UI
|
||||
|
||||
1. Open Visual Studio
|
||||
2. Go to **Extensions → Manage Extensions**
|
||||
3. Click **Installed** tab
|
||||
4. Look for "Roslyn Bridge"
|
||||
|
||||
### 2. Test HTTP Endpoint
|
||||
|
||||
```powershell
|
||||
# Health check
|
||||
curl -X POST http://localhost:59123/health -H "Content-Type: application/json" -d "{}"
|
||||
|
||||
# Get projects
|
||||
curl -X POST http://localhost:59123/query -H "Content-Type: application/json" -d '{"queryType":"getprojects"}'
|
||||
```
|
||||
|
||||
### 3. Check Output Window
|
||||
|
||||
In Visual Studio:
|
||||
1. Open **View → Output**
|
||||
2. Select "RoslynBridge" from the dropdown
|
||||
3. Should see startup messages when opening a solution
|
||||
|
||||
### 4. Check Event Viewer (if issues)
|
||||
|
||||
1. Open Event Viewer
|
||||
2. Navigate to: **Windows Logs → Application**
|
||||
3. Filter by Source: "VSPackage"
|
||||
4. Look for RoslynBridge-related messages
|
||||
|
||||
## Debugging
|
||||
|
||||
### Extension Won't Load
|
||||
|
||||
**Symptoms:**
|
||||
- Extension shows in "Installed" but HTTP endpoint doesn't work
|
||||
- No output in RoslynBridge Output window
|
||||
|
||||
**Solutions:**
|
||||
|
||||
1. **Check Extension Loading:**
|
||||
```powershell
|
||||
# In Visual Studio, open Developer PowerShell
|
||||
Get-VSPackage | Where-Object { $_.Name -like "*Roslyn*" }
|
||||
```
|
||||
|
||||
2. **Enable Diagnostic Logging:**
|
||||
- Run Visual Studio with: `devenv.exe /log`
|
||||
- Check log at: `%APPDATA%\Microsoft\VisualStudio\17.0_XXXXX\ActivityLog.xml`
|
||||
|
||||
3. **Reset Visual Studio:**
|
||||
```cmd
|
||||
devenv.exe /ResetSettings
|
||||
```
|
||||
|
||||
### Port 59123 Already in Use
|
||||
|
||||
```powershell
|
||||
# Check what's using the port
|
||||
netstat -ano | findstr :59123
|
||||
|
||||
# Kill the process if needed
|
||||
taskkill /PID <PID> /F
|
||||
```
|
||||
|
||||
### Extension Loads But Crashes
|
||||
|
||||
Check the ActivityLog:
|
||||
```powershell
|
||||
# Open ActivityLog with formatting
|
||||
code "$env:APPDATA\Microsoft\VisualStudio\17.0_875bdf7a\ActivityLog.xml"
|
||||
```
|
||||
|
||||
Look for entries with:
|
||||
- `Type="Error"`
|
||||
- `Source="RoslynBridge"`
|
||||
|
||||
## Development Workflow
|
||||
|
||||
### Quick Iteration During Development
|
||||
|
||||
```powershell
|
||||
# 1. Make code changes
|
||||
|
||||
# 2. Quick reinstall (no need to close VS manually)
|
||||
.\cleanup-and-reinstall.ps1
|
||||
|
||||
# 3. Open Visual Studio and test
|
||||
```
|
||||
|
||||
### Debugging the Extension
|
||||
|
||||
1. Open the RoslynBridge solution in Visual Studio
|
||||
2. Set breakpoints in your code
|
||||
3. Press **F5** (or Debug → Start Debugging)
|
||||
4. This launches a new "Experimental Instance" of VS
|
||||
5. Open a solution in the experimental instance
|
||||
6. Breakpoints will hit in the original VS instance
|
||||
|
||||
### Testing in Production VS (Not Experimental)
|
||||
|
||||
```powershell
|
||||
# Install in production VS
|
||||
.\cleanup-and-reinstall.ps1
|
||||
|
||||
# Attach debugger from another VS instance
|
||||
# 1. Open a second Visual Studio
|
||||
# 2. Debug → Attach to Process
|
||||
# 3. Select "devenv.exe" (the first VS)
|
||||
# 4. Set breakpoints in the RoslynBridge code
|
||||
```
|
||||
|
||||
## Uninstalling
|
||||
|
||||
### Complete Removal
|
||||
|
||||
```powershell
|
||||
# Use the cleanup script
|
||||
.\cleanup-and-reinstall.ps1 -Action Clean
|
||||
|
||||
# OR manually remove via Extensions Manager
|
||||
# 1. Open Visual Studio
|
||||
# 2. Extensions → Manage Extensions
|
||||
# 3. Find "Roslyn Bridge"
|
||||
# 4. Click "Uninstall"
|
||||
# 5. Restart Visual Studio
|
||||
```
|
||||
|
||||
### Remove All Traces
|
||||
|
||||
```powershell
|
||||
# Remove extension
|
||||
Remove-Item "C:\Users\AJ\AppData\Local\Microsoft\VisualStudio\17.0_875bdf7a\Extensions\ulxnn4r3.rql" -Recurse -Force
|
||||
|
||||
# Remove all VS caches (nuclear option)
|
||||
Remove-Item "C:\Users\AJ\AppData\Local\Microsoft\VisualStudio\17.0_875bdf7a\ComponentModelCache" -Recurse -Force
|
||||
Remove-Item "C:\Users\AJ\AppData\Local\Microsoft\VisualStudio\17.0_875bdf7a\Extensions\ExtensionMetadataCache.sqlite" -Force
|
||||
Remove-Item "C:\Users\AJ\AppData\Local\Microsoft\VisualStudio\17.0_875bdf7a\Extensions\ExtensionMetadata.mpack" -Force
|
||||
|
||||
# Reset Visual Studio
|
||||
devenv.exe /ResetSettings
|
||||
```
|
||||
|
||||
## Troubleshooting Quick Reference
|
||||
|
||||
| Issue | Solution |
|
||||
|-------|----------|
|
||||
| Extension not visible in UI | `.\cleanup-and-reinstall.ps1` |
|
||||
| "Already installed" error | `.\cleanup-and-reinstall.ps1 -Action Clean` then reinstall |
|
||||
| Port 59123 not responding | Check extension loaded in Output window |
|
||||
| VS won't close | Use Task Manager to end `devenv.exe` |
|
||||
| Build fails | Check MSBuild output, ensure .NET SDK installed |
|
||||
| Install fails | Run as Administrator, check VSIXInstaller.exe exists |
|
||||
| Extension crashes on load | Check ActivityLog.xml for errors |
|
||||
|
||||
## File Locations Reference
|
||||
|
||||
```
|
||||
Extension Installation:
|
||||
C:\Users\AJ\AppData\Local\Microsoft\VisualStudio\17.0_875bdf7a\Extensions\ulxnn4r3.rql\
|
||||
|
||||
Cache Files:
|
||||
C:\Users\AJ\AppData\Local\Microsoft\VisualStudio\17.0_875bdf7a\Extensions\ExtensionMetadataCache.sqlite
|
||||
C:\Users\AJ\AppData\Local\Microsoft\VisualStudio\17.0_875bdf7a\Extensions\ExtensionMetadata.mpack
|
||||
C:\Users\AJ\AppData\Local\Microsoft\VisualStudio\17.0_875bdf7a\ComponentModelCache\
|
||||
|
||||
Activity Log:
|
||||
C:\Users\AJ\AppData\Roaming\Microsoft\VisualStudio\17.0_875bdf7a\ActivityLog.xml
|
||||
|
||||
Build Output:
|
||||
C:\Users\AJ\Desktop\RoslynBridge\RoslynBridge\bin\Release\
|
||||
|
||||
VSIX File:
|
||||
C:\Users\AJ\Desktop\RoslynBridge\RoslynBridge\bin\Release\RoslynBridge.vsix
|
||||
```
|
||||
|
||||
## Getting Help
|
||||
|
||||
If issues persist:
|
||||
|
||||
1. Check the ActivityLog.xml for detailed error messages
|
||||
2. Run Visual Studio with logging: `devenv.exe /log`
|
||||
3. Check Windows Event Viewer for crashes
|
||||
4. Try the "nuclear option": Complete removal + reinstall
|
||||
5. Check that .NET Framework 4.8 is installed
|
||||
6. Ensure Visual Studio 2022 Community/Pro/Enterprise (17.0+)
|
||||
@@ -74,12 +74,14 @@
|
||||
|
||||
<!-- Services -->
|
||||
<Compile Include="Services\BaseRoslynService.cs" />
|
||||
<Compile Include="Services\ConfigurationService.cs" />
|
||||
<Compile Include="Services\DiagnosticsService.cs" />
|
||||
<Compile Include="Services\DocumentQueryService.cs" />
|
||||
<Compile Include="Services\IRoslynQueryService.cs" />
|
||||
<Compile Include="Services\IWorkspaceProvider.cs" />
|
||||
<Compile Include="Services\ProjectOperationsService.cs" />
|
||||
<Compile Include="Services\RefactoringService.cs" />
|
||||
<Compile Include="Services\RegistrationService.cs" />
|
||||
<Compile Include="Services\RoslynQueryService.cs" />
|
||||
<Compile Include="Services\SymbolQueryService.cs" />
|
||||
<Compile Include="Services\WorkspaceProvider.cs" />
|
||||
@@ -87,6 +89,10 @@
|
||||
<None Include="source.extension.vsixmanifest">
|
||||
<SubType>Designer</SubType>
|
||||
</None>
|
||||
<Content Include="appsettings.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
<IncludeInVSIX>true</IncludeInVSIX>
|
||||
</Content>
|
||||
<Content Include="README.txt">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
<IncludeInVSIX>true</IncludeInVSIX>
|
||||
@@ -95,6 +101,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ namespace RoslynBridge
|
||||
{
|
||||
public const string PackageGuidString = "b2c3d4e5-f6a7-4b5c-9d8e-0f1a2b3c4d5e";
|
||||
private BridgeServer? _bridgeServer;
|
||||
private Services.RegistrationService? _registrationService;
|
||||
|
||||
protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress<ServiceProgressData> progress)
|
||||
{
|
||||
@@ -29,6 +30,10 @@ namespace RoslynBridge
|
||||
_bridgeServer = new BridgeServer(this);
|
||||
await _bridgeServer.StartAsync();
|
||||
|
||||
// Register with WebAPI
|
||||
_registrationService = new Services.RegistrationService(this, _bridgeServer.Port);
|
||||
await _registrationService.RegisterAsync();
|
||||
|
||||
await base.InitializeAsync(cancellationToken, progress);
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -43,6 +48,8 @@ namespace RoslynBridge
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_registrationService?.UnregisterAsync().Wait(TimeSpan.FromSeconds(5));
|
||||
_registrationService?.Dispose();
|
||||
_bridgeServer?.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
|
||||
@@ -18,12 +18,14 @@ namespace RoslynBridge.Server
|
||||
private readonly AsyncPackage _package;
|
||||
private readonly IRoslynQueryService _queryService;
|
||||
private bool _isRunning;
|
||||
private readonly int _port;
|
||||
private int _port;
|
||||
|
||||
public BridgeServer(AsyncPackage package, int port = ServerConstants.DefaultPort)
|
||||
public int Port => _port;
|
||||
|
||||
public BridgeServer(AsyncPackage package, int? startPort = null)
|
||||
{
|
||||
_package = package;
|
||||
_port = port;
|
||||
_port = startPort ?? ConfigurationService.Instance.DefaultPort;
|
||||
_queryService = new RoslynQueryService(package);
|
||||
}
|
||||
|
||||
@@ -38,12 +40,36 @@ namespace RoslynBridge.Server
|
||||
{
|
||||
await _queryService.InitializeAsync();
|
||||
|
||||
_listener = new HttpListener();
|
||||
_listener.Prefixes.Add(ServerConstants.GetServerUrl(_port));
|
||||
_listener.Start();
|
||||
_isRunning = true;
|
||||
// Try to find an available port
|
||||
int maxPort = _port + ConfigurationService.Instance.MaxPortRange;
|
||||
bool started = false;
|
||||
|
||||
System.Diagnostics.Debug.WriteLine($"Roslyn Bridge HTTP server started on port {_port}");
|
||||
for (int port = _port; port < maxPort; port++)
|
||||
{
|
||||
try
|
||||
{
|
||||
_listener = new HttpListener();
|
||||
_listener.Prefixes.Add(ServerConstants.GetServerUrl(port));
|
||||
_listener.Start();
|
||||
_port = port; // Update to the port that worked
|
||||
started = true;
|
||||
System.Diagnostics.Debug.WriteLine($"Roslyn Bridge HTTP server started on port {_port}");
|
||||
break;
|
||||
}
|
||||
catch (HttpListenerException)
|
||||
{
|
||||
// Port is in use, try next one
|
||||
_listener?.Close();
|
||||
_listener = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (!started)
|
||||
{
|
||||
throw new Exception($"Could not find available port in range {_port}-{maxPort}");
|
||||
}
|
||||
|
||||
_isRunning = true;
|
||||
|
||||
// Start listening for requests in the background
|
||||
#pragma warning disable CS4014
|
||||
@@ -95,6 +121,8 @@ namespace RoslynBridge.Server
|
||||
return;
|
||||
}
|
||||
|
||||
var path = context.Request.Url?.AbsolutePath ?? "/";
|
||||
|
||||
if (context.Request.HttpMethod != "POST")
|
||||
{
|
||||
await RespondWithError(response, 405, "Only POST requests are supported");
|
||||
@@ -110,7 +138,7 @@ namespace RoslynBridge.Server
|
||||
}
|
||||
|
||||
// Route request
|
||||
var queryResponse = await RouteRequestAsync(context.Request.Url?.AbsolutePath ?? "/", request);
|
||||
var queryResponse = await RouteRequestAsync(path, request);
|
||||
response.StatusCode = queryResponse.Success ? 200 : 400;
|
||||
await WriteResponseAsync(response, queryResponse);
|
||||
}
|
||||
@@ -196,6 +224,22 @@ namespace RoslynBridge.Server
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task WriteRawResponseAsync(HttpListenerResponse response, string contentType, string content)
|
||||
{
|
||||
try
|
||||
{
|
||||
response.ContentType = contentType;
|
||||
var buffer = Encoding.UTF8.GetBytes(content);
|
||||
response.ContentLength64 = buffer.Length;
|
||||
await response.OutputStream.WriteAsync(buffer, 0, buffer.Length);
|
||||
response.Close();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"Error writing raw response: {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_isRunning = false;
|
||||
|
||||
85
RoslynBridge/Services/ConfigurationService.cs
Normal file
85
RoslynBridge/Services/ConfigurationService.cs
Normal file
@@ -0,0 +1,85 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace RoslynBridge.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// Service for reading configuration from appsettings.json
|
||||
/// </summary>
|
||||
public class ConfigurationService
|
||||
{
|
||||
private static ConfigurationService? _instance;
|
||||
private readonly RoslynBridgeConfig _config;
|
||||
|
||||
private ConfigurationService()
|
||||
{
|
||||
_config = LoadConfiguration();
|
||||
}
|
||||
|
||||
public static ConfigurationService Instance => _instance ??= new ConfigurationService();
|
||||
|
||||
public string WebApiUrl => _config.RoslynBridge.WebApiUrl;
|
||||
public int DefaultPort => _config.RoslynBridge.DefaultPort;
|
||||
public int MaxPortRange => _config.RoslynBridge.MaxPortRange;
|
||||
public int HeartbeatIntervalSeconds => _config.RoslynBridge.HeartbeatIntervalSeconds;
|
||||
|
||||
private RoslynBridgeConfig LoadConfiguration()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Get the directory where the extension is installed
|
||||
var assemblyPath = Assembly.GetExecutingAssembly().Location;
|
||||
var assemblyDir = Path.GetDirectoryName(assemblyPath);
|
||||
var configPath = Path.Combine(assemblyDir ?? "", "appsettings.json");
|
||||
|
||||
if (File.Exists(configPath))
|
||||
{
|
||||
var json = File.ReadAllText(configPath);
|
||||
var config = JsonSerializer.Deserialize<RoslynBridgeConfig>(json, new JsonSerializerOptions
|
||||
{
|
||||
PropertyNameCaseInsensitive = true
|
||||
});
|
||||
|
||||
if (config != null)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"Loaded configuration from {configPath}");
|
||||
return config;
|
||||
}
|
||||
}
|
||||
|
||||
System.Diagnostics.Debug.WriteLine($"Configuration file not found at {configPath}, using defaults");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"Error loading configuration: {ex.Message}, using defaults");
|
||||
}
|
||||
|
||||
// Return default configuration
|
||||
return new RoslynBridgeConfig
|
||||
{
|
||||
RoslynBridge = new RoslynBridgeSettings
|
||||
{
|
||||
WebApiUrl = "http://localhost:5001",
|
||||
DefaultPort = 59123,
|
||||
MaxPortRange = 10,
|
||||
HeartbeatIntervalSeconds = 60
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class RoslynBridgeConfig
|
||||
{
|
||||
public RoslynBridgeSettings RoslynBridge { get; set; } = new();
|
||||
}
|
||||
|
||||
public class RoslynBridgeSettings
|
||||
{
|
||||
public string WebApiUrl { get; set; } = "http://localhost:5001";
|
||||
public int DefaultPort { get; set; } = 59123;
|
||||
public int MaxPortRange { get; set; } = 10;
|
||||
public int HeartbeatIntervalSeconds { get; set; } = 60;
|
||||
}
|
||||
}
|
||||
138
RoslynBridge/Services/RegistrationService.cs
Normal file
138
RoslynBridge/Services/RegistrationService.cs
Normal file
@@ -0,0 +1,138 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.Shell;
|
||||
|
||||
namespace RoslynBridge.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// Service for registering this VS instance with the WebAPI
|
||||
/// </summary>
|
||||
public class RegistrationService : IDisposable
|
||||
{
|
||||
private readonly string _webApiUrl;
|
||||
private readonly HttpClient _httpClient;
|
||||
private readonly int _port;
|
||||
private readonly AsyncPackage _package;
|
||||
private System.Threading.Timer? _heartbeatTimer;
|
||||
private bool _isRegistered;
|
||||
|
||||
public RegistrationService(AsyncPackage package, int port)
|
||||
{
|
||||
_package = package;
|
||||
_port = port;
|
||||
_webApiUrl = ConfigurationService.Instance.WebApiUrl;
|
||||
_httpClient = new HttpClient { Timeout = TimeSpan.FromSeconds(5) };
|
||||
}
|
||||
|
||||
public async Task RegisterAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var dte = await GetDTEAsync();
|
||||
var solutionPath = dte?.Solution?.FullName;
|
||||
var solutionName = string.IsNullOrEmpty(solutionPath)
|
||||
? null
|
||||
: System.IO.Path.GetFileNameWithoutExtension(solutionPath);
|
||||
|
||||
var registrationData = new
|
||||
{
|
||||
port = _port,
|
||||
processId = Process.GetCurrentProcess().Id,
|
||||
solutionPath = string.IsNullOrEmpty(solutionPath) ? null : solutionPath,
|
||||
solutionName = solutionName,
|
||||
projects = new string[] { } // TODO: Get project names
|
||||
};
|
||||
|
||||
var json = JsonSerializer.Serialize(registrationData);
|
||||
var content = new StringContent(json, Encoding.UTF8, "application/json");
|
||||
|
||||
var response = await _httpClient.PostAsync($"{_webApiUrl}/api/instances/register", content);
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
_isRegistered = true;
|
||||
Debug.WriteLine($"Successfully registered with WebAPI at {_webApiUrl}");
|
||||
|
||||
// Start heartbeat timer (configurable interval)
|
||||
var heartbeatInterval = TimeSpan.FromSeconds(ConfigurationService.Instance.HeartbeatIntervalSeconds);
|
||||
_heartbeatTimer = new System.Threading.Timer(
|
||||
async _ => await SendHeartbeatAsync(),
|
||||
null,
|
||||
heartbeatInterval,
|
||||
heartbeatInterval);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.WriteLine($"Failed to register with WebAPI: {response.StatusCode}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"Error registering with WebAPI: {ex.Message}");
|
||||
// Don't throw - registration is optional, VS extension should work standalone
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SendHeartbeatAsync()
|
||||
{
|
||||
if (!_isRegistered)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
var processId = Process.GetCurrentProcess().Id;
|
||||
var response = await _httpClient.PostAsync(
|
||||
$"{_webApiUrl}/api/instances/heartbeat/{processId}",
|
||||
null);
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
Debug.WriteLine($"Heartbeat failed: {response.StatusCode}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"Error sending heartbeat: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task UnregisterAsync()
|
||||
{
|
||||
if (!_isRegistered)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
var processId = Process.GetCurrentProcess().Id;
|
||||
var response = await _httpClient.PostAsync(
|
||||
$"{_webApiUrl}/api/instances/unregister/{processId}",
|
||||
null);
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
Debug.WriteLine("Successfully unregistered from WebAPI");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.WriteLine($"Error unregistering from WebAPI: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<EnvDTE.DTE?> GetDTEAsync()
|
||||
{
|
||||
await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
|
||||
return await _package.GetServiceAsync(typeof(EnvDTE.DTE)) as EnvDTE.DTE;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_heartbeatTimer?.Dispose();
|
||||
_httpClient?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
8
RoslynBridge/appsettings.json
Normal file
8
RoslynBridge/appsettings.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"RoslynBridge": {
|
||||
"WebApiUrl": "http://localhost:5001",
|
||||
"DefaultPort": 59123,
|
||||
"MaxPortRange": 10,
|
||||
"HeartbeatIntervalSeconds": 60
|
||||
}
|
||||
}
|
||||
337
RoslynBridge/cleanup-and-reinstall.ps1
Normal file
337
RoslynBridge/cleanup-and-reinstall.ps1
Normal file
@@ -0,0 +1,337 @@
|
||||
#Requires -Version 5.0
|
||||
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Cleanup and reinstall the RoslynBridge Visual Studio extension
|
||||
|
||||
.DESCRIPTION
|
||||
This script automates the process of:
|
||||
1. Closing Visual Studio
|
||||
2. Removing the extension cache
|
||||
3. Deleting the installed extension
|
||||
4. Rebuilding and reinstalling the extension
|
||||
|
||||
.PARAMETER Action
|
||||
The action to perform: Clean, Reinstall, or Both (default)
|
||||
|
||||
.PARAMETER SkipBuild
|
||||
Skip the build step when reinstalling
|
||||
|
||||
.EXAMPLE
|
||||
.\cleanup-and-reinstall.ps1
|
||||
Cleans and reinstalls the extension
|
||||
|
||||
.EXAMPLE
|
||||
.\cleanup-and-reinstall.ps1 -Action Clean
|
||||
Only removes the extension and cache
|
||||
|
||||
.EXAMPLE
|
||||
.\cleanup-and-reinstall.ps1 -Action Reinstall -SkipBuild
|
||||
Reinstalls without rebuilding (uses existing VSIX)
|
||||
#>
|
||||
|
||||
param(
|
||||
[Parameter(Mandatory=$false)]
|
||||
[ValidateSet("Clean", "Reinstall", "Both")]
|
||||
[string]$Action = "Both",
|
||||
|
||||
[Parameter(Mandatory=$false)]
|
||||
[switch]$SkipBuild
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
# Colors for output
|
||||
function Write-ColorOutput {
|
||||
param(
|
||||
[string]$Message,
|
||||
[string]$Color = "White"
|
||||
)
|
||||
Write-Host $Message -ForegroundColor $Color
|
||||
}
|
||||
|
||||
function Write-Step {
|
||||
param([string]$Message)
|
||||
Write-ColorOutput "`n>>> $Message" "Cyan"
|
||||
}
|
||||
|
||||
function Write-Success {
|
||||
param([string]$Message)
|
||||
Write-ColorOutput " ✓ $Message" "Green"
|
||||
}
|
||||
|
||||
function Write-Warning {
|
||||
param([string]$Message)
|
||||
Write-ColorOutput " ⚠ $Message" "Yellow"
|
||||
}
|
||||
|
||||
function Write-ErrorMsg {
|
||||
param([string]$Message)
|
||||
Write-ColorOutput " ✗ $Message" "Red"
|
||||
}
|
||||
|
||||
# Configuration
|
||||
$VSInstancePath = "C:\Users\AJ\AppData\Local\Microsoft\VisualStudio\17.0_875bdf7a"
|
||||
$ExtensionsPath = Join-Path $VSInstancePath "Extensions"
|
||||
$ExtensionFolder = "ulxnn4r3.rql"
|
||||
$ExtensionFullPath = Join-Path $ExtensionsPath $ExtensionFolder
|
||||
$ProjectPath = $PSScriptRoot
|
||||
$SolutionFile = Join-Path $ProjectPath "RoslynBridge.csproj"
|
||||
|
||||
Write-ColorOutput "`n========================================" "Cyan"
|
||||
Write-ColorOutput " RoslynBridge Extension Manager" "Cyan"
|
||||
Write-ColorOutput "========================================`n" "Cyan"
|
||||
|
||||
# Step 1: Close Visual Studio
|
||||
function Stop-VisualStudio {
|
||||
Write-Step "Closing Visual Studio..."
|
||||
|
||||
$vsProcesses = Get-Process | Where-Object { $_.Name -like "devenv*" }
|
||||
|
||||
if ($vsProcesses) {
|
||||
Write-ColorOutput " Found $($vsProcesses.Count) Visual Studio instance(s) running" "Gray"
|
||||
|
||||
foreach ($proc in $vsProcesses) {
|
||||
try {
|
||||
Write-ColorOutput " Closing Visual Studio (PID: $($proc.Id))..." "Gray"
|
||||
$proc.CloseMainWindow() | Out-Null
|
||||
Start-Sleep -Seconds 2
|
||||
|
||||
if (!$proc.HasExited) {
|
||||
Write-ColorOutput " Force closing..." "Gray"
|
||||
$proc.Kill()
|
||||
Start-Sleep -Seconds 1
|
||||
}
|
||||
|
||||
Write-Success "Visual Studio closed"
|
||||
}
|
||||
catch {
|
||||
Write-Warning "Failed to close Visual Studio: $_"
|
||||
}
|
||||
}
|
||||
|
||||
# Wait for processes to fully terminate
|
||||
Start-Sleep -Seconds 3
|
||||
}
|
||||
else {
|
||||
Write-Success "Visual Studio is not running"
|
||||
}
|
||||
}
|
||||
|
||||
# Step 2: Clean extension and cache
|
||||
function Clear-ExtensionCache {
|
||||
Write-Step "Cleaning extension cache..."
|
||||
|
||||
$filesToDelete = @(
|
||||
(Join-Path $ExtensionsPath "ExtensionMetadataCache.sqlite"),
|
||||
(Join-Path $ExtensionsPath "ExtensionMetadata.mpack"),
|
||||
(Join-Path $VSInstancePath "ComponentModelCache")
|
||||
)
|
||||
|
||||
foreach ($file in $filesToDelete) {
|
||||
if (Test-Path $file) {
|
||||
try {
|
||||
Remove-Item $file -Recurse -Force -ErrorAction Stop
|
||||
Write-Success "Deleted: $(Split-Path $file -Leaf)"
|
||||
}
|
||||
catch {
|
||||
Write-Warning "Could not delete $file: $_"
|
||||
}
|
||||
}
|
||||
else {
|
||||
Write-ColorOutput " Skipped (not found): $(Split-Path $file -Leaf)" "Gray"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function Remove-InstalledExtension {
|
||||
Write-Step "Removing installed extension..."
|
||||
|
||||
if (Test-Path $ExtensionFullPath) {
|
||||
try {
|
||||
Remove-Item $ExtensionFullPath -Recurse -Force -ErrorAction Stop
|
||||
Write-Success "Extension removed: $ExtensionFolder"
|
||||
}
|
||||
catch {
|
||||
Write-ErrorMsg "Failed to remove extension: $_"
|
||||
return $false
|
||||
}
|
||||
}
|
||||
else {
|
||||
Write-Success "Extension not installed"
|
||||
}
|
||||
|
||||
return $true
|
||||
}
|
||||
|
||||
# Step 3: Build the extension
|
||||
function Build-Extension {
|
||||
Write-Step "Building RoslynBridge extension..."
|
||||
|
||||
if (!(Test-Path $SolutionFile)) {
|
||||
Write-ErrorMsg "Solution file not found: $SolutionFile"
|
||||
return $false
|
||||
}
|
||||
|
||||
Write-ColorOutput " Building in Release mode..." "Gray"
|
||||
|
||||
try {
|
||||
# Use MSBuild to build the project
|
||||
$msbuildPath = "C:\Program Files\Microsoft Visual Studio\2022\Community\MSBuild\Current\Bin\MSBuild.exe"
|
||||
|
||||
if (!(Test-Path $msbuildPath)) {
|
||||
Write-ErrorMsg "MSBuild not found at: $msbuildPath"
|
||||
return $false
|
||||
}
|
||||
|
||||
$buildOutput = & $msbuildPath $SolutionFile /t:Rebuild /p:Configuration=Release /v:minimal 2>&1
|
||||
|
||||
if ($LASTEXITCODE -eq 0) {
|
||||
Write-Success "Build completed successfully"
|
||||
return $true
|
||||
}
|
||||
else {
|
||||
Write-ErrorMsg "Build failed with exit code: $LASTEXITCODE"
|
||||
Write-ColorOutput "Build output:" "Gray"
|
||||
$buildOutput | ForEach-Object { Write-ColorOutput " $_" "Gray" }
|
||||
return $false
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Write-ErrorMsg "Build error: $_"
|
||||
return $false
|
||||
}
|
||||
}
|
||||
|
||||
# Step 4: Install the extension
|
||||
function Install-Extension {
|
||||
Write-Step "Installing extension..."
|
||||
|
||||
# Find the VSIX file
|
||||
$vsixFiles = Get-ChildItem -Path $ProjectPath -Filter "*.vsix" -Recurse | Where-Object {
|
||||
$_.FullName -like "*\bin\Release\*" -or $_.FullName -like "*\bin\Debug\*"
|
||||
} | Sort-Object LastWriteTime -Descending
|
||||
|
||||
if (!$vsixFiles) {
|
||||
Write-ErrorMsg "No VSIX file found. Please build the project first."
|
||||
return $false
|
||||
}
|
||||
|
||||
$vsixFile = $vsixFiles[0].FullName
|
||||
Write-ColorOutput " Found VSIX: $vsixFile" "Gray"
|
||||
|
||||
# Use VSIXInstaller to install
|
||||
$vsixInstallerPath = "C:\Program Files\Microsoft Visual Studio\2022\Community\Common7\IDE\VSIXInstaller.exe"
|
||||
|
||||
if (!(Test-Path $vsixInstallerPath)) {
|
||||
Write-ErrorMsg "VSIXInstaller not found at: $vsixInstallerPath"
|
||||
return $false
|
||||
}
|
||||
|
||||
Write-ColorOutput " Installing extension..." "Gray"
|
||||
|
||||
try {
|
||||
$installArgs = @("/quiet", "/admin", $vsixFile)
|
||||
$installOutput = & $vsixInstallerPath $installArgs 2>&1
|
||||
|
||||
if ($LASTEXITCODE -eq 0 -or $LASTEXITCODE -eq 1001) {
|
||||
# 1001 = already installed (which is fine, we're reinstalling)
|
||||
Write-Success "Extension installed successfully"
|
||||
return $true
|
||||
}
|
||||
else {
|
||||
Write-ErrorMsg "Installation failed with exit code: $LASTEXITCODE"
|
||||
$installOutput | ForEach-Object { Write-ColorOutput " $_" "Gray" }
|
||||
return $false
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Write-ErrorMsg "Installation error: $_"
|
||||
return $false
|
||||
}
|
||||
}
|
||||
|
||||
# Step 5: Verify installation
|
||||
function Test-ExtensionInstalled {
|
||||
Write-Step "Verifying installation..."
|
||||
|
||||
if (Test-Path $ExtensionFullPath) {
|
||||
$dllPath = Join-Path $ExtensionFullPath "RoslynBridge.dll"
|
||||
if (Test-Path $dllPath) {
|
||||
Write-Success "Extension files found at: $ExtensionFolder"
|
||||
|
||||
# Check file version
|
||||
try {
|
||||
$dllInfo = Get-Item $dllPath
|
||||
$version = $dllInfo.VersionInfo.FileVersion
|
||||
Write-ColorOutput " Version: $version" "Gray"
|
||||
}
|
||||
catch {
|
||||
Write-ColorOutput " Version: Unable to determine" "Gray"
|
||||
}
|
||||
|
||||
return $true
|
||||
}
|
||||
}
|
||||
|
||||
Write-Warning "Extension files not found"
|
||||
return $false
|
||||
}
|
||||
|
||||
# Main execution
|
||||
try {
|
||||
$startTime = Get-Date
|
||||
|
||||
if ($Action -eq "Clean" -or $Action -eq "Both") {
|
||||
Stop-VisualStudio
|
||||
Clear-ExtensionCache
|
||||
$cleanSuccess = Remove-InstalledExtension
|
||||
|
||||
if (!$cleanSuccess) {
|
||||
Write-ErrorMsg "`nCleanup failed!"
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
if ($Action -eq "Reinstall" -or $Action -eq "Both") {
|
||||
if (!$SkipBuild) {
|
||||
$buildSuccess = Build-Extension
|
||||
if (!$buildSuccess) {
|
||||
Write-ErrorMsg "`nBuild failed! Cannot reinstall."
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
else {
|
||||
Write-Warning "Skipping build as requested"
|
||||
}
|
||||
|
||||
$installSuccess = Install-Extension
|
||||
if (!$installSuccess) {
|
||||
Write-ErrorMsg "`nInstallation failed!"
|
||||
exit 1
|
||||
}
|
||||
|
||||
Test-ExtensionInstalled | Out-Null
|
||||
}
|
||||
|
||||
$elapsed = (Get-Date) - $startTime
|
||||
|
||||
Write-ColorOutput "`n========================================" "Cyan"
|
||||
Write-ColorOutput " ✓ Completed in $($elapsed.TotalSeconds.ToString('F1')) seconds" "Green"
|
||||
Write-ColorOutput "========================================`n" "Cyan"
|
||||
|
||||
if ($Action -eq "Reinstall" -or $Action -eq "Both") {
|
||||
Write-ColorOutput "Next steps:" "Yellow"
|
||||
Write-ColorOutput " 1. Open Visual Studio" "White"
|
||||
Write-ColorOutput " 2. Go to Extensions → Manage Extensions" "White"
|
||||
Write-ColorOutput " 3. Check 'Installed' tab for 'Roslyn Bridge'" "White"
|
||||
Write-ColorOutput " 4. Open a solution and verify the extension loads" "White"
|
||||
Write-ColorOutput " 5. Test: curl -X POST http://localhost:59123/health -H 'Content-Type: application/json' -d '{}'" "White"
|
||||
Write-ColorOutput ""
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Write-ErrorMsg "`nUnexpected error: $_"
|
||||
Write-ColorOutput $_.ScriptStackTrace "Gray"
|
||||
exit 1
|
||||
}
|
||||
267
SKILL.md
Normal file
267
SKILL.md
Normal file
@@ -0,0 +1,267 @@
|
||||
---
|
||||
name: roslyn-bridge
|
||||
description: Use this for C# code analysis, querying .NET projects, finding symbols, getting diagnostics, or any Roslyn/semantic analysis tasks using the bridge server
|
||||
---
|
||||
|
||||
# Roslyn Bridge API Guide
|
||||
|
||||
Use this guide when accessing the Roslyn Bridge for C# code analysis.
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
┌─────────────┐ REST API ┌────────────────────┐ HTTP ┌─────────────────────┐
|
||||
│ Claude │ ◄─────────────────► │ Web API (:5001) │ ◄────────────► │ VS Plugin (:59123) │
|
||||
│ AI │ │ Middleware │ │ Roslyn Bridge │
|
||||
└─────────────┘ └────────────────────┘ └─────────────────────┘
|
||||
```
|
||||
|
||||
## Recommended: Web API (Port 5001)
|
||||
|
||||
**Base URL**: `http://localhost:5001`
|
||||
|
||||
The Web API provides a modern RESTful interface with:
|
||||
- Clean REST endpoints with query parameters
|
||||
- Full Swagger documentation at `/`
|
||||
- Request/response history tracking at `/api/history`
|
||||
- CORS support for web applications
|
||||
- Better error handling and logging
|
||||
|
||||
### Web API Endpoints
|
||||
|
||||
**Health & Info:**
|
||||
- `GET /api/health` - Check health of both Web API and VS plugin
|
||||
- `GET /api/health/ping` - Simple ping
|
||||
|
||||
**Roslyn Operations:**
|
||||
- `GET /api/roslyn/projects` - Get all projects
|
||||
- `GET /api/roslyn/solution/overview` - Solution statistics
|
||||
- `GET /api/roslyn/diagnostics?filePath={path}` - Get errors/warnings
|
||||
- `GET /api/roslyn/symbol?filePath={path}&line={line}&column={col}` - Get symbol info
|
||||
- `GET /api/roslyn/references?filePath={path}&line={line}&column={col}` - Find references
|
||||
- `GET /api/roslyn/symbol/search?symbolName={name}&kind={kind}` - Search symbols
|
||||
- `POST /api/roslyn/query` - Execute any query (fallback to raw queries)
|
||||
- `POST /api/roslyn/format` - Format document
|
||||
- `POST /api/roslyn/project/package/add` - Add NuGet package
|
||||
- `POST /api/roslyn/project/build` - Build project
|
||||
|
||||
**History:**
|
||||
- `GET /api/history` - All history entries
|
||||
- `GET /api/history/{id}` - Specific entry
|
||||
- `GET /api/history/recent?count=50` - Recent entries
|
||||
- `GET /api/history/stats` - Statistics
|
||||
- `DELETE /api/history` - Clear history
|
||||
|
||||
## Alternative: Direct VS Plugin Access (Port 59123)
|
||||
|
||||
**Base URL**: `http://localhost:59123`
|
||||
|
||||
Direct access to the Visual Studio plugin (use only if Web API is unavailable):
|
||||
- **Endpoints**: `/query`, `/health`
|
||||
- **Method**: POST only
|
||||
- **Content-Type**: application/json
|
||||
|
||||
## ⚠️ CRITICAL: Command Syntax Rules
|
||||
|
||||
**ALWAYS use curl with the Bash tool. NEVER pipe curl output to PowerShell.**
|
||||
|
||||
### ✅ CORRECT: Using Web API (Recommended)
|
||||
|
||||
```bash
|
||||
# Health check
|
||||
curl http://localhost:5001/api/health
|
||||
|
||||
# Get all projects (returns full file paths)
|
||||
curl http://localhost:5001/api/roslyn/projects
|
||||
|
||||
# Get solution overview
|
||||
curl http://localhost:5001/api/roslyn/solution/overview
|
||||
|
||||
# Get diagnostics for entire solution
|
||||
curl http://localhost:5001/api/roslyn/diagnostics
|
||||
|
||||
# Get diagnostics for specific file
|
||||
curl "http://localhost:5001/api/roslyn/diagnostics?filePath=C:/path/to/file.cs"
|
||||
|
||||
# Get symbol at position (use paths from /projects response)
|
||||
curl "http://localhost:5001/api/roslyn/symbol?filePath=C:/path/to/file.cs&line=10&column=5"
|
||||
|
||||
# Find all references
|
||||
curl "http://localhost:5001/api/roslyn/references?filePath=C:/path/to/file.cs&line=18&column=30"
|
||||
|
||||
# Search for symbols
|
||||
curl "http://localhost:5001/api/roslyn/symbol/search?symbolName=MyClass&kind=class"
|
||||
|
||||
# Get recent history
|
||||
curl http://localhost:5001/api/history/recent?count=10
|
||||
|
||||
# Get history statistics
|
||||
curl http://localhost:5001/api/history/stats
|
||||
```
|
||||
|
||||
**IMPORTANT curl syntax rules:**
|
||||
- For GET requests, no `-X GET` needed
|
||||
- Use quotes around URLs with query parameters
|
||||
- Forward slashes `/` work in file paths (Windows accepts both `/` and `\`)
|
||||
- **DO NOT pipe to PowerShell** - the JSON output is already formatted
|
||||
|
||||
### Using Direct VS Plugin Access (Fallback)
|
||||
|
||||
```bash
|
||||
# Health check - verify VS plugin is running
|
||||
curl -X POST http://localhost:59123/health -H "Content-Type: application/json" -d "{}"
|
||||
|
||||
# Get projects via POST
|
||||
curl -X POST http://localhost:59123/query -H "Content-Type: application/json" -d "{\"queryType\":\"getprojects\"}"
|
||||
|
||||
# Get symbol at position
|
||||
curl -X POST http://localhost:59123/query -H "Content-Type: application/json" -d "{\"queryType\":\"getsymbol\",\"filePath\":\"C:\\\\path\\\\to\\\\file.cs\",\"line\":10,\"column\":5}"
|
||||
```
|
||||
|
||||
**Note:** In POST requests to VS plugin, escape backslashes: `\\\\` becomes `\\` in JSON
|
||||
|
||||
## Quick Reference: All Endpoints
|
||||
|
||||
### Query Endpoints
|
||||
|
||||
| Endpoint | Required Fields | Optional Fields | Description |
|
||||
|----------|----------------|-----------------|-------------|
|
||||
| `getprojects` | - | - | Get all projects in the solution |
|
||||
| `getdocument` | `filePath` | - | Get document information for a specific file |
|
||||
| `getsymbol` | `filePath`, `line`, `column` | - | Get symbol information at a specific position |
|
||||
| `getsemanticmodel` | `filePath` | - | Verify semantic model availability (not serializable) |
|
||||
| `getsyntaxtree` | `filePath` | - | Get the syntax tree (source code) for a file |
|
||||
| `getdiagnostics` | - | `filePath` | Get compilation errors and warnings |
|
||||
| `findreferences` | `filePath`, `line`, `column` | - | Find all references to a symbol |
|
||||
| `findsymbol` | `symbolName` | `parameters.kind` | Find symbols by name (supports filtering by kind) |
|
||||
| `gettypemembers` | `symbolName` | `parameters.includeInherited` | Get all members of a type |
|
||||
| `gettypehierarchy` | `symbolName` | `parameters.direction` | Get base types or derived types |
|
||||
| `findimplementations` | `symbolName` OR `filePath`+`line`+`column` | - | Find implementations of an interface/abstract member |
|
||||
| `getnamespacetypes` | `symbolName` | - | Get all types in a namespace |
|
||||
| `getcallhierarchy` | `filePath`, `line`, `column` | `parameters.direction` | Get callers or callees of a method |
|
||||
| `getsolutionoverview` | - | - | Get high-level solution statistics |
|
||||
| `getsymbolcontext` | `filePath`, `line`, `column` | - | Get contextual information about a symbol's location |
|
||||
| `searchcode` | `symbolName` (regex) | `parameters.scope` | Search for code patterns using regex |
|
||||
|
||||
### Editing Endpoints
|
||||
|
||||
| Endpoint | Required Fields | Optional Fields | Description |
|
||||
|----------|----------------|-----------------|-------------|
|
||||
| `formatdocument` | `filePath` | - | Format a document according to coding style |
|
||||
| `organizeusings` | `filePath` | - | Sort and remove unused using statements |
|
||||
| `renamesymbol` | `filePath`, `line`, `column`, `parameters.newName` | - | Rename a symbol across the solution |
|
||||
| `addmissingusing` | `filePath`, `line`, `column` | - | Add missing using statement for a symbol |
|
||||
| `applycodefix` | `filePath`, `line`, `column` | - | Apply available code fixes at a position |
|
||||
|
||||
### Project Operation Endpoints
|
||||
|
||||
| Endpoint | Required Fields | Optional Fields | Description |
|
||||
|----------|----------------|-----------------|-------------|
|
||||
| `addnugetpackage` | `projectName`, `packageName` | `version` | Add a NuGet package to a project |
|
||||
| `removenugetpackage` | `projectName`, `packageName` | - | Remove a NuGet package from a project |
|
||||
| `buildproject` | `projectName` | `configuration` | Build a project or solution |
|
||||
| `cleanproject` | `projectName` | - | Clean build output |
|
||||
| `restorepackages` | `projectName` | - | Restore NuGet packages |
|
||||
| `createdirectory` | `directoryPath` | - | Create a new directory |
|
||||
|
||||
## Common Workflow Examples
|
||||
|
||||
### Typical Code Analysis Workflow
|
||||
|
||||
```bash
|
||||
# Step 1: Check if services are healthy
|
||||
curl http://localhost:5001/api/health
|
||||
|
||||
# Step 2: Get all projects and their files
|
||||
curl http://localhost:5001/api/roslyn/projects
|
||||
# Response includes: {"data": [{"documents": ["C:/Full/Path/To/File.cs", ...]}]}
|
||||
|
||||
# Step 3: Use the full paths from Step 2 in subsequent queries
|
||||
FILE="C:/Users/AJ/Desktop/MyProject/Program.cs"
|
||||
|
||||
# Get diagnostics (errors/warnings) for a file
|
||||
curl "http://localhost:5001/api/roslyn/diagnostics?filePath=$FILE"
|
||||
|
||||
# Get symbol information at a specific location
|
||||
curl "http://localhost:5001/api/roslyn/symbol?filePath=$FILE&line=15&column=10"
|
||||
|
||||
# Find all references to that symbol
|
||||
curl "http://localhost:5001/api/roslyn/references?filePath=$FILE&line=15&column=10"
|
||||
|
||||
# Step 4: View your query history
|
||||
curl http://localhost:5001/api/history/recent?count=5
|
||||
|
||||
# Step 5: Get statistics about your queries
|
||||
curl http://localhost:5001/api/history/stats
|
||||
```
|
||||
|
||||
### Advanced Query Examples
|
||||
|
||||
```bash
|
||||
# Search for all classes with "Service" in the name
|
||||
curl "http://localhost:5001/api/roslyn/symbol/search?symbolName=Service&kind=class"
|
||||
|
||||
# Get solution-wide statistics
|
||||
curl http://localhost:5001/api/roslyn/solution/overview
|
||||
|
||||
# For complex queries not available as REST endpoints, use POST /query
|
||||
curl -X POST http://localhost:5001/api/roslyn/query \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"queryType":"searchcode","symbolName":".*Controller","parameters":{"scope":"classes"}}'
|
||||
|
||||
# Build a project
|
||||
curl -X POST "http://localhost:5001/api/roslyn/project/build?projectName=MyProject"
|
||||
|
||||
# Add NuGet package
|
||||
curl -X POST "http://localhost:5001/api/roslyn/project/package/add?projectName=MyProject&packageName=Newtonsoft.Json&version=13.0.3"
|
||||
```
|
||||
|
||||
## Response Format
|
||||
|
||||
All endpoints return JSON in this format:
|
||||
|
||||
**Success:**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "Optional message",
|
||||
"data": { /* Response data varies by endpoint */ },
|
||||
"error": null
|
||||
}
|
||||
```
|
||||
|
||||
**Error:**
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"message": null,
|
||||
"data": null,
|
||||
"error": "Error description"
|
||||
}
|
||||
```
|
||||
|
||||
## Important Notes
|
||||
|
||||
- Line numbers are **1-based** (first line is 1)
|
||||
- Column numbers are **0-based** (first column is 0)
|
||||
- **Use full paths from `/api/roslyn/projects` response** in other endpoints
|
||||
- Forward slashes `/` work in file paths (Windows accepts both `/` and `\`)
|
||||
- Both the Web API (port 5000) and VS Plugin (port 59123) must be running
|
||||
- All workspace modifications use VS threading model
|
||||
- Request history is automatically tracked in the Web API
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**"Failed to connect" error:**
|
||||
1. Check if Web API is running: `curl http://localhost:5001/api/health/ping`
|
||||
2. Check if VS Plugin is running: `curl -X POST http://localhost:59123/health -H "Content-Type: application/json" -d "{}"`
|
||||
3. Ensure Visual Studio is open with a solution loaded
|
||||
|
||||
**"vsPluginStatus": "Disconnected":**
|
||||
- The VS Plugin (port 59123) is not running
|
||||
- Open Visual Studio and ensure the RoslynBridge extension is loaded
|
||||
|
||||
**Empty or null data:**
|
||||
- Ensure you're using the correct file paths from `/api/roslyn/projects`
|
||||
- Verify line/column numbers are within the file bounds
|
||||
- Check that the file is part of the currently open VS solution
|
||||
372
rb.ps1
Normal file
372
rb.ps1
Normal file
@@ -0,0 +1,372 @@
|
||||
# Roslyn Bridge PowerShell helper
|
||||
#
|
||||
# Purpose: Fast, low‑token wrapper around the Roslyn Bridge Web API (port 5001)
|
||||
# and the legacy VS plugin (port 59123). Auto-detects which is running
|
||||
# and provides concise commands for common operations.
|
||||
#
|
||||
# Usage examples:
|
||||
# .\rb.ps1 health
|
||||
# .\rb.ps1 projects
|
||||
# .\rb.ps1 overview
|
||||
# .\rb.ps1 diagnostics -FilePath C:/path/to/File.cs
|
||||
# .\rb.ps1 symbol -FilePath C:/path/to/File.cs -Line 12 -Column 4
|
||||
# .\rb.ps1 references -FilePath C:/path/to/File.cs -Line 12 -Column 4
|
||||
# .\rb.ps1 search -SymbolName MyService -Kind class
|
||||
# .\rb.ps1 build -ProjectName MyProject
|
||||
# .\rb.ps1 package-add -ProjectName MyProject -PackageName Newtonsoft.Json -Version 13.0.3
|
||||
# .\rb.ps1 history-recent -Count 10
|
||||
# .\rb.ps1 query -BodyJson '{"queryType":"searchcode","symbolName":".*Controller"}'
|
||||
#
|
||||
# Output:
|
||||
# - Compressed JSON by default (minimal tokens)
|
||||
# - Add -Pretty for indented JSON
|
||||
# - Add -Raw to return the raw response string
|
||||
|
||||
[CmdletBinding(PositionalBinding=$true)]
|
||||
param(
|
||||
[Parameter(Position=0, Mandatory=$true)]
|
||||
[ValidateSet(
|
||||
'health','projects','overview','diagnostics','symbol','references','search',
|
||||
'build','package-add','history','history-recent','history-stats',
|
||||
'plugin-health','solution-overview'
|
||||
)]
|
||||
[string]$Command,
|
||||
|
||||
# Common params
|
||||
[string]$BaseUrl, # Overrides auto-detected base URL
|
||||
[switch]$NoDetect, # Skip auto-detection
|
||||
[int]$TimeoutSec = 6, # Short default to stay snappy
|
||||
[switch]$Raw, # Emit raw response string
|
||||
[switch]$Pretty, # Indented JSON output
|
||||
|
||||
# Query params
|
||||
[string]$FilePath,
|
||||
[int]$Line,
|
||||
[int]$Column,
|
||||
[string]$SymbolName,
|
||||
[ValidateSet('class','method','property','field','interface','enum','struct','namespace','event','any')]
|
||||
[string]$Kind = 'any',
|
||||
|
||||
# Project ops
|
||||
[string]$ProjectName,
|
||||
[string]$Configuration,
|
||||
[string]$PackageName,
|
||||
[string]$Version,
|
||||
|
||||
# History
|
||||
[int]$Count = 25,
|
||||
|
||||
# Solution overview summary options
|
||||
[ValidateSet('json','text','yaml')]
|
||||
[string]$Format = 'json',
|
||||
[int]$Top = 5,
|
||||
[bool]$IncludeNamespaces = $true
|
||||
)
|
||||
|
||||
set-strictmode -version latest
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
function Write-Err {
|
||||
param([string]$Msg)
|
||||
Write-Error -Message $Msg
|
||||
}
|
||||
|
||||
function Get-JsonOutput {
|
||||
param(
|
||||
[Parameter(Mandatory=$true)] $Data
|
||||
)
|
||||
if ($Raw) {
|
||||
# If we were given an object, compress to a compact string; if it's already a string, emit it.
|
||||
if ($Data -is [string]) { return $Data }
|
||||
return ($Data | ConvertTo-Json -Depth 50 -Compress)
|
||||
}
|
||||
$json = $Data | ConvertTo-Json -Depth 50 -Compress
|
||||
if ($Pretty) { $json = ($Data | ConvertTo-Json -Depth 50) }
|
||||
return $json
|
||||
}
|
||||
|
||||
function Test-Url {
|
||||
param(
|
||||
[string]$Url,
|
||||
[ValidateSet('GET','POST')][string]$Method = 'GET'
|
||||
)
|
||||
try {
|
||||
if ($Method -eq 'GET') {
|
||||
$resp = Invoke-WebRequest -Method GET -Uri $Url -TimeoutSec $TimeoutSec
|
||||
} else {
|
||||
$resp = Invoke-WebRequest -Method POST -Uri $Url -TimeoutSec $TimeoutSec -ContentType 'application/json' -Body '{}'
|
||||
}
|
||||
return $true
|
||||
} catch {
|
||||
return $false
|
||||
}
|
||||
}
|
||||
|
||||
function Get-BridgeEndpoints {
|
||||
# Decide which base URL to use.
|
||||
# Priority: explicit -BaseUrl -> Web API (5001) if healthy -> Plugin (59123) if healthy
|
||||
$web = 'http://localhost:5001'
|
||||
$plugin = 'http://localhost:59123'
|
||||
|
||||
if ($BaseUrl) {
|
||||
return @{ BaseUrl = $BaseUrl; Mode = 'explicit' }
|
||||
}
|
||||
|
||||
if ($NoDetect) {
|
||||
return @{ BaseUrl = $web; Mode = 'web' }
|
||||
}
|
||||
|
||||
if (Test-Url -Url "$web/api/health/ping" -Method GET) {
|
||||
return @{ BaseUrl = $web; Mode = 'web' }
|
||||
}
|
||||
|
||||
if (Test-Url -Url "$plugin/health" -Method POST) {
|
||||
return @{ BaseUrl = $plugin; Mode = 'plugin' }
|
||||
}
|
||||
|
||||
# Fallback to web
|
||||
return @{ BaseUrl = $web; Mode = 'web' }
|
||||
}
|
||||
|
||||
function Invoke-WebApi {
|
||||
param(
|
||||
[string]$BaseUrl,
|
||||
[ValidateSet('GET','POST')][string]$Method,
|
||||
[string]$Path,
|
||||
[hashtable]$Query,
|
||||
$Body
|
||||
)
|
||||
$uri = [System.UriBuilder]::new($BaseUrl)
|
||||
if ($Path.StartsWith('/')) { $uri.Path = $Path } else { $uri.Path = "$($uri.Path.TrimEnd('/'))/$Path" }
|
||||
if ($Query) {
|
||||
$qs = [System.Web.HttpUtility]::ParseQueryString([string]::Empty)
|
||||
foreach ($k in $Query.Keys) {
|
||||
if ($null -ne $Query[$k] -and $Query[$k] -ne '') { $qs[$k] = [string]$Query[$k] }
|
||||
}
|
||||
$uri.Query = $qs.ToString()
|
||||
}
|
||||
if ($Raw) {
|
||||
if ($Method -eq 'GET') {
|
||||
return (Invoke-WebRequest -Method GET -Uri $uri.Uri -TimeoutSec $TimeoutSec).Content
|
||||
} else {
|
||||
$bodyStr = if ($Body -is [string]) { $Body } else { ($Body | ConvertTo-Json -Depth 50 -Compress) }
|
||||
return (Invoke-WebRequest -Method POST -Uri $uri.Uri -TimeoutSec $TimeoutSec -ContentType 'application/json' -Body $bodyStr).Content
|
||||
}
|
||||
}
|
||||
if ($Method -eq 'GET') {
|
||||
return Invoke-RestMethod -Method GET -Uri $uri.Uri -TimeoutSec $TimeoutSec
|
||||
} else {
|
||||
if ($Body -is [string]) {
|
||||
return Invoke-RestMethod -Method POST -Uri $uri.Uri -TimeoutSec $TimeoutSec -ContentType 'application/json' -Body $Body
|
||||
} else {
|
||||
return Invoke-RestMethod -Method POST -Uri $uri.Uri -TimeoutSec $TimeoutSec -ContentType 'application/json' -Body ($Body | ConvertTo-Json -Depth 50 -Compress)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function Invoke-PluginApi {
|
||||
param(
|
||||
[string]$BaseUrl,
|
||||
[string]$Path, # '/query' or '/health'
|
||||
$Body # hashtable or string
|
||||
)
|
||||
$uri = "$BaseUrl$Path"
|
||||
if ($Raw) {
|
||||
$bodyStr = if ($Body -is [string]) { $Body } else { ($Body | ConvertTo-Json -Depth 50 -Compress) }
|
||||
return (Invoke-WebRequest -Method POST -Uri $uri -TimeoutSec $TimeoutSec -ContentType 'application/json' -Body $bodyStr).Content
|
||||
}
|
||||
if ($Body -is [string]) {
|
||||
return Invoke-RestMethod -Method POST -Uri $uri -TimeoutSec $TimeoutSec -ContentType 'application/json' -Body $Body
|
||||
} else {
|
||||
return Invoke-RestMethod -Method POST -Uri $uri -TimeoutSec $TimeoutSec -ContentType 'application/json' -Body ($Body | ConvertTo-Json -Depth 50 -Compress)
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# Main dispatch
|
||||
#
|
||||
|
||||
$endpoint = Get-BridgeEndpoints
|
||||
$mode = $endpoint.Mode
|
||||
$base = $endpoint.BaseUrl
|
||||
|
||||
try {
|
||||
switch ($Command) {
|
||||
'health' {
|
||||
if ($mode -eq 'web' -or $mode -eq 'explicit') {
|
||||
$resp = Invoke-WebApi -BaseUrl $base -Method GET -Path '/api/health'
|
||||
$out = Get-JsonOutput -Data $resp
|
||||
Write-Output $out
|
||||
} else {
|
||||
$resp = Invoke-PluginApi -BaseUrl $base -Path '/health' -Body '{}'
|
||||
$out = Get-JsonOutput -Data $resp
|
||||
Write-Output $out
|
||||
}
|
||||
}
|
||||
'projects' {
|
||||
if ($mode -eq 'web' -or $mode -eq 'explicit') {
|
||||
$resp = Invoke-WebApi -BaseUrl $base -Method GET -Path '/api/roslyn/projects'
|
||||
} else {
|
||||
$resp = Invoke-PluginApi -BaseUrl $base -Path '/query' -Body @{ queryType = 'getprojects' }
|
||||
}
|
||||
Write-Output (Get-JsonOutput -Data $resp)
|
||||
}
|
||||
'overview' {
|
||||
if ($mode -eq 'web' -or $mode -eq 'explicit') {
|
||||
$resp = Invoke-WebApi -BaseUrl $base -Method GET -Path '/api/roslyn/solution/overview'
|
||||
} else {
|
||||
$resp = Invoke-PluginApi -BaseUrl $base -Path '/query' -Body @{ queryType = 'getsolutionoverview' }
|
||||
}
|
||||
Write-Output (Get-JsonOutput -Data $resp)
|
||||
}
|
||||
'diagnostics' {
|
||||
if ($mode -eq 'web' -or $mode -eq 'explicit') {
|
||||
$q = @{}
|
||||
if ($FilePath) { $q.filePath = $FilePath }
|
||||
$resp = Invoke-WebApi -BaseUrl $base -Method GET -Path '/api/roslyn/diagnostics' -Query $q
|
||||
} else {
|
||||
$body = @{ queryType = 'getdiagnostics' }
|
||||
if ($FilePath) { $body.filePath = $FilePath }
|
||||
$resp = Invoke-PluginApi -BaseUrl $base -Path '/query' -Body $body
|
||||
}
|
||||
Write-Output (Get-JsonOutput -Data $resp)
|
||||
}
|
||||
'symbol' {
|
||||
if (-not $FilePath -or -not $PSBoundParameters.ContainsKey('Line') -or -not $PSBoundParameters.ContainsKey('Column')) {
|
||||
Write-Err 'symbol requires -FilePath, -Line, -Column'; exit 2
|
||||
}
|
||||
if ($mode -eq 'web' -or $mode -eq 'explicit') {
|
||||
$q = @{ filePath=$FilePath; line=$Line; column=$Column }
|
||||
$resp = Invoke-WebApi -BaseUrl $base -Method GET -Path '/api/roslyn/symbol' -Query $q
|
||||
} else {
|
||||
$body = @{ queryType='getsymbol'; filePath=$FilePath; line=$Line; column=$Column }
|
||||
$resp = Invoke-PluginApi -BaseUrl $base -Path '/query' -Body $body
|
||||
}
|
||||
Write-Output (Get-JsonOutput -Data $resp)
|
||||
}
|
||||
'references' {
|
||||
if (-not $FilePath -or -not $PSBoundParameters.ContainsKey('Line') -or -not $PSBoundParameters.ContainsKey('Column')) {
|
||||
Write-Err 'references requires -FilePath, -Line, -Column'; exit 2
|
||||
}
|
||||
if ($mode -eq 'web' -or $mode -eq 'explicit') {
|
||||
$q = @{ filePath=$FilePath; line=$Line; column=$Column }
|
||||
$resp = Invoke-WebApi -BaseUrl $base -Method GET -Path '/api/roslyn/references' -Query $q
|
||||
} else {
|
||||
$body = @{ queryType='findreferences'; filePath=$FilePath; line=$Line; column=$Column }
|
||||
$resp = Invoke-PluginApi -BaseUrl $base -Path '/query' -Body $body
|
||||
}
|
||||
Write-Output (Get-JsonOutput -Data $resp)
|
||||
}
|
||||
'search' {
|
||||
if (-not $SymbolName) { Write-Err 'search requires -SymbolName'; exit 2 }
|
||||
if ($mode -eq 'web' -or $mode -eq 'explicit') {
|
||||
$q = @{ symbolName=$SymbolName }
|
||||
if ($Kind -and $Kind -ne 'any') { $q.kind = $Kind }
|
||||
$resp = Invoke-WebApi -BaseUrl $base -Method GET -Path '/api/roslyn/symbol/search' -Query $q
|
||||
} else {
|
||||
$parameters = @{}
|
||||
if ($Kind -and $Kind -ne 'any') { $parameters.kind = $Kind }
|
||||
$body = @{ queryType='findsymbol'; symbolName=$SymbolName }
|
||||
if ($parameters.Count -gt 0) { $body.parameters = $parameters }
|
||||
$resp = Invoke-PluginApi -BaseUrl $base -Path '/query' -Body $body
|
||||
}
|
||||
Write-Output (Get-JsonOutput -Data $resp)
|
||||
}
|
||||
'build' {
|
||||
if (-not $ProjectName) { Write-Err 'build requires -ProjectName'; exit 2 }
|
||||
if ($mode -eq 'web' -or $mode -eq 'explicit') {
|
||||
$q = @{ projectName=$ProjectName }
|
||||
if ($Configuration) { $q.configuration = $Configuration }
|
||||
$resp = Invoke-WebApi -BaseUrl $base -Method POST -Path '/api/roslyn/project/build' -Query $q
|
||||
} else {
|
||||
$body = @{ queryType='buildproject'; projectName=$ProjectName }
|
||||
if ($Configuration) { $body.configuration = $Configuration }
|
||||
$resp = Invoke-PluginApi -BaseUrl $base -Path '/query' -Body $body
|
||||
}
|
||||
Write-Output (Get-JsonOutput -Data $resp)
|
||||
}
|
||||
'package-add' {
|
||||
if (-not $ProjectName -or -not $PackageName) { Write-Err 'package-add requires -ProjectName and -PackageName'; exit 2 }
|
||||
if ($mode -eq 'web' -or $mode -eq 'explicit') {
|
||||
$q = @{ projectName=$ProjectName; packageName=$PackageName }
|
||||
if ($Version) { $q.version = $Version }
|
||||
$resp = Invoke-WebApi -BaseUrl $base -Method POST -Path '/api/roslyn/project/package/add' -Query $q
|
||||
} else {
|
||||
$body = @{ queryType='addnugetpackage'; projectName=$ProjectName; packageName=$PackageName }
|
||||
if ($Version) { $body.version = $Version }
|
||||
$resp = Invoke-PluginApi -BaseUrl $base -Path '/query' -Body $body
|
||||
}
|
||||
Write-Output (Get-JsonOutput -Data $resp)
|
||||
}
|
||||
'history' {
|
||||
if ($mode -eq 'web' -or $mode -eq 'explicit') {
|
||||
$resp = Invoke-WebApi -BaseUrl $base -Method GET -Path '/api/history'
|
||||
} else {
|
||||
Write-Err 'history is only available via Web API (port 5001).'; exit 2
|
||||
}
|
||||
Write-Output (Get-JsonOutput -Data $resp)
|
||||
}
|
||||
'history-recent' {
|
||||
if ($mode -eq 'web' -or $mode -eq 'explicit') {
|
||||
$q = @{ count = $Count }
|
||||
$resp = Invoke-WebApi -BaseUrl $base -Method GET -Path '/api/history/recent' -Query $q
|
||||
} else {
|
||||
Write-Err 'history-recent is only available via Web API (port 5001).'; exit 2
|
||||
}
|
||||
Write-Output (Get-JsonOutput -Data $resp)
|
||||
}
|
||||
'history-stats' {
|
||||
if ($mode -eq 'web' -or $mode -eq 'explicit') {
|
||||
$resp = Invoke-WebApi -BaseUrl $base -Method GET -Path '/api/history/stats'
|
||||
} else {
|
||||
Write-Err 'history-stats is only available via Web API (port 5001).'; exit 2
|
||||
}
|
||||
Write-Output (Get-JsonOutput -Data $resp)
|
||||
}
|
||||
'plugin-health' {
|
||||
$pluginBase = if ($BaseUrl) { $BaseUrl } else { 'http://localhost:59123' }
|
||||
$resp = Invoke-PluginApi -BaseUrl $pluginBase -Path '/health' -Body '{}'
|
||||
Write-Output (Get-JsonOutput -Data $resp)
|
||||
}
|
||||
'solution-overview' {
|
||||
if ($mode -ne 'web' -and $mode -ne 'explicit') {
|
||||
Write-Err 'solution-overview requires the Web API (port 5001).'; exit 2
|
||||
}
|
||||
$q = @{ topNProjects=$Top; includeNamespaces=$IncludeNamespaces; format=$Format }
|
||||
# For text/yaml formats, we want the raw response as-is
|
||||
if ($Format -eq 'json') {
|
||||
$resp = Invoke-WebApi -BaseUrl $base -Method GET -Path '/api/roslyn/solution/overview/summary' -Query $q
|
||||
Write-Output (Get-JsonOutput -Data $resp)
|
||||
} else {
|
||||
# Build query string properly
|
||||
$qs = [System.Web.HttpUtility]::ParseQueryString([string]::Empty)
|
||||
$qs['topNProjects'] = [string]$Top
|
||||
$qs['includeNamespaces'] = [string]$IncludeNamespaces
|
||||
$qs['format'] = $Format
|
||||
$uri = [System.UriBuilder]::new("$base/api/roslyn/solution/overview/summary")
|
||||
$uri.Query = $qs.ToString()
|
||||
$content = (Invoke-WebRequest -Method GET -Uri $uri.Uri -TimeoutSec $TimeoutSec).Content
|
||||
Write-Output $content
|
||||
}
|
||||
}
|
||||
default {
|
||||
Write-Err "Unknown command: $Command"; exit 2
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
$msg = $_.Exception.Message
|
||||
$respProp = $_.Exception.PSObject.Properties['Response']
|
||||
if ($respProp -and $respProp.Value) {
|
||||
try {
|
||||
$resp = $respProp.Value
|
||||
$stream = $resp.GetResponseStream()
|
||||
if ($stream) {
|
||||
$reader = New-Object System.IO.StreamReader($stream)
|
||||
$content = $reader.ReadToEnd()
|
||||
if ($content) { $msg = "$msg`n$content" }
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
Write-Err $msg
|
||||
exit 1
|
||||
}
|
||||
Reference in New Issue
Block a user