From 532d2b62594510c635bfeeec6d3df2090d7c0662 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 25 Feb 2026 12:02:58 +0000 Subject: [PATCH 1/8] Initial plan From b57e967a8e336b2daaf38075a1266d6cd65abe0b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 25 Feb 2026 12:06:41 +0000 Subject: [PATCH 2/8] Add REST API discoverability: 'api' command and help text Co-authored-by: waldekmastykarz <11164679+waldekmastykarz@users.noreply.github.com> --- DevProxy/Commands/ApiCommand.cs | 68 ++++++++++++++++++++++++++++ DevProxy/Commands/DevProxyCommand.cs | 3 +- 2 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 DevProxy/Commands/ApiCommand.cs diff --git a/DevProxy/Commands/ApiCommand.cs b/DevProxy/Commands/ApiCommand.cs new file mode 100644 index 00000000..576c3a7b --- /dev/null +++ b/DevProxy/Commands/ApiCommand.cs @@ -0,0 +1,68 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using DevProxy.Abstractions.Proxy; +using DevProxy.Abstractions.Utils; +using System.CommandLine; +using System.Text.Json; + +namespace DevProxy.Commands; + +sealed class ApiCommand : Command +{ + private readonly IProxyConfiguration _proxyConfiguration; + + public ApiCommand(IProxyConfiguration proxyConfiguration) : + base("api", "Display Dev Proxy API information for runtime management") + { + _proxyConfiguration = proxyConfiguration; + ConfigureCommand(); + } + + private void ConfigureCommand() + { + SetAction(parseResult => + { + PrintApiInfo(); + }); + } + + private void PrintApiInfo() + { + var ipAddress = _proxyConfiguration.IPAddress ?? "127.0.0.1"; + var apiPort = _proxyConfiguration.ApiPort; + var baseUrl = $"http://{ipAddress}:{apiPort}"; + + var apiInfo = new ApiInfo + { + BaseUrl = baseUrl, + SwaggerUrl = $"{baseUrl}/swagger/v1/swagger.json", + Endpoints = + [ + new ApiEndpointInfo { Method = "GET", Path = "/proxy", Description = "Get proxy status" }, + new ApiEndpointInfo { Method = "POST", Path = "/proxy", Description = "Update proxy status (e.g. start/stop recording)" }, + new ApiEndpointInfo { Method = "POST", Path = "/proxy/mockRequest", Description = "Issue a mock request" }, + new ApiEndpointInfo { Method = "POST", Path = "/proxy/stopProxy", Description = "Stop the proxy" }, + new ApiEndpointInfo { Method = "POST", Path = "/proxy/jwtToken", Description = "Create a JWT token" }, + new ApiEndpointInfo { Method = "GET", Path = "/proxy/rootCertificate", Description = "Get the root certificate" } + ] + }; + + Console.WriteLine(JsonSerializer.Serialize(apiInfo, ProxyUtils.JsonSerializerOptions)); + } +} + +sealed class ApiInfo +{ + public string BaseUrl { get; set; } = string.Empty; + public string SwaggerUrl { get; set; } = string.Empty; + public ApiEndpointInfo[] Endpoints { get; set; } = []; +} + +sealed class ApiEndpointInfo +{ + public string Method { get; set; } = string.Empty; + public string Path { get; set; } = string.Empty; + public string Description { get; set; } = string.Empty; +} diff --git a/DevProxy/Commands/DevProxyCommand.cs b/DevProxy/Commands/DevProxyCommand.cs index c5c4b8b3..14796b27 100644 --- a/DevProxy/Commands/DevProxyCommand.cs +++ b/DevProxy/Commands/DevProxyCommand.cs @@ -145,7 +145,7 @@ public DevProxyCommand( IProxyConfiguration proxyConfiguration, IServiceProvider serviceProvider, UpdateNotification updateNotification, - ILogger logger) : base("Start Dev Proxy") + ILogger logger) : base("Start Dev Proxy\n\nAPI:\n Dev Proxy exposes a REST API for runtime management.\n OpenAPI spec: http://{ip-address}:{api-port}/swagger\n Use --api-port to configure (default: 8897).\n Run 'devproxy api' for more information.") { _serviceProvider = serviceProvider; _plugins = plugins; @@ -443,6 +443,7 @@ private void ConfigureCommand() var commands = new List { + ActivatorUtilities.CreateInstance(_serviceProvider), ActivatorUtilities.CreateInstance(_serviceProvider), ActivatorUtilities.CreateInstance(_serviceProvider), ActivatorUtilities.CreateInstance(_serviceProvider), From 4524789c420540c6c93371155129048a41b5c4c9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 25 Feb 2026 13:18:37 +0000 Subject: [PATCH 3/8] Rename 'api' to 'api show' for consistency with existing verb subcommands Co-authored-by: waldekmastykarz <11164679+waldekmastykarz@users.noreply.github.com> --- DevProxy/Commands/ApiCommand.cs | 11 +++++++++-- DevProxy/Commands/DevProxyCommand.cs | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/DevProxy/Commands/ApiCommand.cs b/DevProxy/Commands/ApiCommand.cs index 576c3a7b..197190ae 100644 --- a/DevProxy/Commands/ApiCommand.cs +++ b/DevProxy/Commands/ApiCommand.cs @@ -5,6 +5,7 @@ using DevProxy.Abstractions.Proxy; using DevProxy.Abstractions.Utils; using System.CommandLine; +using System.CommandLine.Parsing; using System.Text.Json; namespace DevProxy.Commands; @@ -14,7 +15,7 @@ sealed class ApiCommand : Command private readonly IProxyConfiguration _proxyConfiguration; public ApiCommand(IProxyConfiguration proxyConfiguration) : - base("api", "Display Dev Proxy API information for runtime management") + base("api", "Manage Dev Proxy API information") { _proxyConfiguration = proxyConfiguration; ConfigureCommand(); @@ -22,10 +23,16 @@ public ApiCommand(IProxyConfiguration proxyConfiguration) : private void ConfigureCommand() { - SetAction(parseResult => + var apiShowCommand = new Command("show", "Display Dev Proxy API information for runtime management"); + apiShowCommand.SetAction(parseResult => { PrintApiInfo(); }); + + this.AddCommands(new List + { + apiShowCommand + }.OrderByName()); } private void PrintApiInfo() diff --git a/DevProxy/Commands/DevProxyCommand.cs b/DevProxy/Commands/DevProxyCommand.cs index 14796b27..73779c89 100644 --- a/DevProxy/Commands/DevProxyCommand.cs +++ b/DevProxy/Commands/DevProxyCommand.cs @@ -145,7 +145,7 @@ public DevProxyCommand( IProxyConfiguration proxyConfiguration, IServiceProvider serviceProvider, UpdateNotification updateNotification, - ILogger logger) : base("Start Dev Proxy\n\nAPI:\n Dev Proxy exposes a REST API for runtime management.\n OpenAPI spec: http://{ip-address}:{api-port}/swagger\n Use --api-port to configure (default: 8897).\n Run 'devproxy api' for more information.") + ILogger logger) : base("Start Dev Proxy\n\nAPI:\n Dev Proxy exposes a REST API for runtime management.\n OpenAPI spec: http://{ip-address}:{api-port}/swagger\n Use --api-port to configure (default: 8897).\n Run 'devproxy api show' for more information.") { _serviceProvider = serviceProvider; _plugins = plugins; From 0dbb7f286ca0be91dc3f39c9304511da879d21c8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 25 Feb 2026 13:22:07 +0000 Subject: [PATCH 4/8] Address PR feedback: use Logger, remove redundant default, read port from config Co-authored-by: waldekmastykarz <11164679+waldekmastykarz@users.noreply.github.com> --- DevProxy/Commands/ApiCommand.cs | 9 ++++++--- DevProxy/Commands/DevProxyCommand.cs | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/DevProxy/Commands/ApiCommand.cs b/DevProxy/Commands/ApiCommand.cs index 197190ae..c5bb3945 100644 --- a/DevProxy/Commands/ApiCommand.cs +++ b/DevProxy/Commands/ApiCommand.cs @@ -4,6 +4,7 @@ using DevProxy.Abstractions.Proxy; using DevProxy.Abstractions.Utils; +using Microsoft.Extensions.Logging; using System.CommandLine; using System.CommandLine.Parsing; using System.Text.Json; @@ -13,11 +14,13 @@ namespace DevProxy.Commands; sealed class ApiCommand : Command { private readonly IProxyConfiguration _proxyConfiguration; + private readonly ILogger _logger; - public ApiCommand(IProxyConfiguration proxyConfiguration) : + public ApiCommand(IProxyConfiguration proxyConfiguration, ILogger logger) : base("api", "Manage Dev Proxy API information") { _proxyConfiguration = proxyConfiguration; + _logger = logger; ConfigureCommand(); } @@ -37,7 +40,7 @@ private void ConfigureCommand() private void PrintApiInfo() { - var ipAddress = _proxyConfiguration.IPAddress ?? "127.0.0.1"; + var ipAddress = _proxyConfiguration.IPAddress; var apiPort = _proxyConfiguration.ApiPort; var baseUrl = $"http://{ipAddress}:{apiPort}"; @@ -56,7 +59,7 @@ private void PrintApiInfo() ] }; - Console.WriteLine(JsonSerializer.Serialize(apiInfo, ProxyUtils.JsonSerializerOptions)); + _logger.LogInformation("{ApiInfo}", JsonSerializer.Serialize(apiInfo, ProxyUtils.JsonSerializerOptions)); } } diff --git a/DevProxy/Commands/DevProxyCommand.cs b/DevProxy/Commands/DevProxyCommand.cs index 73779c89..c1b56dde 100644 --- a/DevProxy/Commands/DevProxyCommand.cs +++ b/DevProxy/Commands/DevProxyCommand.cs @@ -145,7 +145,7 @@ public DevProxyCommand( IProxyConfiguration proxyConfiguration, IServiceProvider serviceProvider, UpdateNotification updateNotification, - ILogger logger) : base("Start Dev Proxy\n\nAPI:\n Dev Proxy exposes a REST API for runtime management.\n OpenAPI spec: http://{ip-address}:{api-port}/swagger\n Use --api-port to configure (default: 8897).\n Run 'devproxy api show' for more information.") + ILogger logger) : base($"Start Dev Proxy\n\nAPI:\n Dev Proxy exposes a REST API for runtime management.\n OpenAPI spec: http://{{ip-address}}:{{api-port}}/swagger\n Use --api-port to configure (default: {proxyConfiguration.ApiPort}).\n Run 'devproxy api show' for more information.") { _serviceProvider = serviceProvider; _plugins = plugins; From 9eb163cdeb7a65b144b555896af7f2f415899d60 Mon Sep 17 00:00:00 2001 From: Waldek Mastykarz Date: Wed, 25 Feb 2026 15:54:10 +0100 Subject: [PATCH 5/8] Update DevProxy/Commands/DevProxyCommand.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- DevProxy/Commands/DevProxyCommand.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DevProxy/Commands/DevProxyCommand.cs b/DevProxy/Commands/DevProxyCommand.cs index fa1e18d0..8ec5369e 100644 --- a/DevProxy/Commands/DevProxyCommand.cs +++ b/DevProxy/Commands/DevProxyCommand.cs @@ -197,7 +197,7 @@ public DevProxyCommand( IProxyConfiguration proxyConfiguration, IServiceProvider serviceProvider, UpdateNotification updateNotification, - ILogger logger) : base($"Start Dev Proxy\n\nAPI:\n Dev Proxy exposes a REST API for runtime management.\n OpenAPI spec: http://{{ip-address}}:{{api-port}}/swagger\n Use --api-port to configure (default: {proxyConfiguration.ApiPort}).\n Run 'devproxy api show' for more information.") + ILogger logger) : base($"Start Dev Proxy\n\nAPI:\n Dev Proxy exposes a REST API for runtime management.\n OpenAPI spec: http://{proxyConfiguration.IPAddress ?? "127.0.0.1"}:{proxyConfiguration.ApiPort}/swagger\n Use --api-port to configure (default: {proxyConfiguration.ApiPort}).\n Run 'devproxy api show' for more information.") { _serviceProvider = serviceProvider; _plugins = plugins; From 7835cb351a02ccd4189e02a07ca7e42f4bd7eebb Mon Sep 17 00:00:00 2001 From: Waldek Mastykarz Date: Wed, 25 Feb 2026 15:54:30 +0100 Subject: [PATCH 6/8] Update DevProxy/Commands/ApiCommand.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- DevProxy/Commands/ApiCommand.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/DevProxy/Commands/ApiCommand.cs b/DevProxy/Commands/ApiCommand.cs index c5bb3945..8ad0cd6b 100644 --- a/DevProxy/Commands/ApiCommand.cs +++ b/DevProxy/Commands/ApiCommand.cs @@ -55,7 +55,8 @@ private void PrintApiInfo() new ApiEndpointInfo { Method = "POST", Path = "/proxy/mockRequest", Description = "Issue a mock request" }, new ApiEndpointInfo { Method = "POST", Path = "/proxy/stopProxy", Description = "Stop the proxy" }, new ApiEndpointInfo { Method = "POST", Path = "/proxy/jwtToken", Description = "Create a JWT token" }, - new ApiEndpointInfo { Method = "GET", Path = "/proxy/rootCertificate", Description = "Get the root certificate" } + new ApiEndpointInfo { Method = "GET", Path = "/proxy/rootCertificate", Description = "Get the root certificate" }, + new ApiEndpointInfo { Method = "GET", Path = "/proxy/logs", Description = "Get proxy logs (for detached mode access)" } ] }; From 7b36f20ab7ebb944ed0de68620d17675e1024ba8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 25 Feb 2026 15:07:02 +0000 Subject: [PATCH 7/8] Align output pattern with PR #1548: use _logger.LogInformation() for text output Co-authored-by: waldekmastykarz <11164679+waldekmastykarz@users.noreply.github.com> --- DevProxy/Commands/ApiCommand.cs | 39 ++++++++++++++------------------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/DevProxy/Commands/ApiCommand.cs b/DevProxy/Commands/ApiCommand.cs index 8ad0cd6b..3b0701d3 100644 --- a/DevProxy/Commands/ApiCommand.cs +++ b/DevProxy/Commands/ApiCommand.cs @@ -3,11 +3,9 @@ // See the LICENSE file in the project root for more information. using DevProxy.Abstractions.Proxy; -using DevProxy.Abstractions.Utils; using Microsoft.Extensions.Logging; using System.CommandLine; using System.CommandLine.Parsing; -using System.Text.Json; namespace DevProxy.Commands; @@ -44,33 +42,28 @@ private void PrintApiInfo() var apiPort = _proxyConfiguration.ApiPort; var baseUrl = $"http://{ipAddress}:{apiPort}"; - var apiInfo = new ApiInfo + var endpoints = new[] { - BaseUrl = baseUrl, - SwaggerUrl = $"{baseUrl}/swagger/v1/swagger.json", - Endpoints = - [ - new ApiEndpointInfo { Method = "GET", Path = "/proxy", Description = "Get proxy status" }, - new ApiEndpointInfo { Method = "POST", Path = "/proxy", Description = "Update proxy status (e.g. start/stop recording)" }, - new ApiEndpointInfo { Method = "POST", Path = "/proxy/mockRequest", Description = "Issue a mock request" }, - new ApiEndpointInfo { Method = "POST", Path = "/proxy/stopProxy", Description = "Stop the proxy" }, - new ApiEndpointInfo { Method = "POST", Path = "/proxy/jwtToken", Description = "Create a JWT token" }, - new ApiEndpointInfo { Method = "GET", Path = "/proxy/rootCertificate", Description = "Get the root certificate" }, - new ApiEndpointInfo { Method = "GET", Path = "/proxy/logs", Description = "Get proxy logs (for detached mode access)" } - ] + new ApiEndpointInfo { Method = "GET", Path = "/proxy", Description = "Get proxy status" }, + new ApiEndpointInfo { Method = "POST", Path = "/proxy", Description = "Update proxy status (e.g. start/stop recording)" }, + new ApiEndpointInfo { Method = "POST", Path = "/proxy/mockRequest", Description = "Issue a mock request" }, + new ApiEndpointInfo { Method = "POST", Path = "/proxy/stopProxy", Description = "Stop the proxy" }, + new ApiEndpointInfo { Method = "POST", Path = "/proxy/jwtToken", Description = "Create a JWT token" }, + new ApiEndpointInfo { Method = "GET", Path = "/proxy/rootCertificate", Description = "Get the root certificate" }, + new ApiEndpointInfo { Method = "GET", Path = "/proxy/logs", Description = "Get proxy logs (for detached mode access)" } }; - _logger.LogInformation("{ApiInfo}", JsonSerializer.Serialize(apiInfo, ProxyUtils.JsonSerializerOptions)); + _logger.LogInformation("Base URL: {BaseUrl}", baseUrl); + _logger.LogInformation("OpenAPI spec: {SwaggerUrl}", $"{baseUrl}/swagger/v1/swagger.json"); + _logger.LogInformation(""); + _logger.LogInformation("Endpoints:"); + foreach (var endpoint in endpoints) + { + _logger.LogInformation(" {Method,-6} {Path,-30} {Description}", endpoint.Method, endpoint.Path, endpoint.Description); + } } } -sealed class ApiInfo -{ - public string BaseUrl { get; set; } = string.Empty; - public string SwaggerUrl { get; set; } = string.Empty; - public ApiEndpointInfo[] Endpoints { get; set; } = []; -} - sealed class ApiEndpointInfo { public string Method { get; set; } = string.Empty; From cc00e7cd6617df8662013b12514026ca72f96583 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 26 Feb 2026 12:30:19 +0000 Subject: [PATCH 8/8] Use --output text|json pattern from merged PR #1548 in ApiCommand Co-authored-by: waldekmastykarz <11164679+waldekmastykarz@users.noreply.github.com> --- DevProxy/Commands/ApiCommand.cs | 37 ++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/DevProxy/Commands/ApiCommand.cs b/DevProxy/Commands/ApiCommand.cs index 3b0701d3..f18d5bf0 100644 --- a/DevProxy/Commands/ApiCommand.cs +++ b/DevProxy/Commands/ApiCommand.cs @@ -3,9 +3,11 @@ // See the LICENSE file in the project root for more information. using DevProxy.Abstractions.Proxy; +using DevProxy.Abstractions.Utils; using Microsoft.Extensions.Logging; using System.CommandLine; using System.CommandLine.Parsing; +using System.Text.Json; namespace DevProxy.Commands; @@ -27,7 +29,8 @@ private void ConfigureCommand() var apiShowCommand = new Command("show", "Display Dev Proxy API information for runtime management"); apiShowCommand.SetAction(parseResult => { - PrintApiInfo(); + var outputFormat = parseResult.GetValueOrDefault(DevProxyCommand.OutputOptionName) ?? OutputFormat.Text; + PrintApiInfo(outputFormat); }); this.AddCommands(new List @@ -36,7 +39,7 @@ private void ConfigureCommand() }.OrderByName()); } - private void PrintApiInfo() + private void PrintApiInfo(OutputFormat outputFormat) { var ipAddress = _proxyConfiguration.IPAddress; var apiPort = _proxyConfiguration.ApiPort; @@ -53,13 +56,31 @@ private void PrintApiInfo() new ApiEndpointInfo { Method = "GET", Path = "/proxy/logs", Description = "Get proxy logs (for detached mode access)" } }; - _logger.LogInformation("Base URL: {BaseUrl}", baseUrl); - _logger.LogInformation("OpenAPI spec: {SwaggerUrl}", $"{baseUrl}/swagger/v1/swagger.json"); - _logger.LogInformation(""); - _logger.LogInformation("Endpoints:"); - foreach (var endpoint in endpoints) + if (outputFormat == OutputFormat.Json) { - _logger.LogInformation(" {Method,-6} {Path,-30} {Description}", endpoint.Method, endpoint.Path, endpoint.Description); + var json = JsonSerializer.Serialize(new + { + baseUrl, + swaggerUrl = $"{baseUrl}/swagger/v1/swagger.json", + endpoints = endpoints.Select(e => new + { + method = e.Method, + path = e.Path, + description = e.Description + }) + }, ProxyUtils.JsonSerializerOptions); + _logger.LogStructuredOutput(json); + } + else + { + _logger.LogInformation("Base URL: {BaseUrl}", baseUrl); + _logger.LogInformation("OpenAPI spec: {SwaggerUrl}", $"{baseUrl}/swagger/v1/swagger.json"); + _logger.LogInformation(""); + _logger.LogInformation("Endpoints:"); + foreach (var endpoint in endpoints) + { + _logger.LogInformation(" {Method,-6} {Path,-30} {Description}", endpoint.Method, endpoint.Path, endpoint.Description); + } } } }