Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions dev-proxy/Announcement.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

namespace Microsoft.DevProxy;

static class Announcement
{
private static readonly string announcementUrl = "https://aka.ms/devproxy/announcement";

public static async Task ShowAsync()
{
var announcement = await GetAsync();
if (!string.IsNullOrEmpty(announcement))
{
await Console.Error.WriteLineAsync(announcement);
}
}

public static async Task<string?> GetAsync()
{
try
{
using var client = new HttpClient();
return await client.GetStringAsync(announcementUrl);
}
catch
{
return null;
}
}
}
234 changes: 118 additions & 116 deletions dev-proxy/Program.cs
Original file line number Diff line number Diff line change
@@ -1,116 +1,118 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using Microsoft.DevProxy;
using Microsoft.DevProxy.Abstractions;
using Microsoft.DevProxy.Abstractions.LanguageModel;
using Microsoft.DevProxy.CommandHandlers;
using Microsoft.DevProxy.Logging;
using Microsoft.Extensions.Logging.Console;
using System.CommandLine;

PluginEvents pluginEvents = new();

ILogger BuildLogger()
{
var loggerFactory = LoggerFactory.Create(builder =>
{
builder
.AddConsole(options =>
{
options.FormatterName = "devproxy";
options.LogToStandardErrorThreshold = LogLevel.Warning;
})
.AddConsoleFormatter<ProxyConsoleFormatter, ConsoleFormatterOptions>(options => {
options.IncludeScopes = true;
})
.AddRequestLogger(pluginEvents)
.SetMinimumLevel(ProxyHost.LogLevel ?? ProxyCommandHandler.Configuration.LogLevel);
});
return loggerFactory.CreateLogger("devproxy");
}

var logger = BuildLogger();

var lmClient = new OllamaLanguageModelClient(ProxyCommandHandler.Configuration.LanguageModel, logger);
IProxyContext context = new ProxyContext(ProxyCommandHandler.Configuration, ProxyEngine.Certificate, lmClient);
ProxyHost proxyHost = new();

// this is where the root command is created which contains all commands and subcommands
RootCommand rootCommand = proxyHost.GetRootCommand(logger);

// store the global options that are created automatically for us
// rootCommand doesn't return the global options, so we have to store them manually
string[] globalOptions = ["--version", "--help", "-h", "/h", "-?", "/?"];

// check if any of the global options are present
var hasGlobalOption = args.Any(arg => globalOptions.Contains(arg));

// get the list of available subcommands
var subCommands = rootCommand.Children.OfType<Command>().Select(c => c.Name).ToArray();

// check if any of the subcommands are present
var hasSubCommand = args.Any(arg => subCommands.Contains(arg));

if (hasGlobalOption || hasSubCommand)
{
// we don't need to load plugins if the user is using a global option or using a subcommand, so we can exit early
await rootCommand.InvokeAsync(args);
return;
}

var pluginLoader = new PluginLoader(logger);
PluginLoaderResult loaderResults = await pluginLoader.LoadPluginsAsync(pluginEvents, context);
// have all the plugins init
pluginEvents.RaiseInit(new InitArgs());

var options = loaderResults.ProxyPlugins
.SelectMany(p => p.GetOptions())
// remove duplicates by comparing the option names
.GroupBy(o => o.Name)
.Select(g => g.First())
.ToList();
options.ForEach(rootCommand.AddOption);
// register all plugin commands
loaderResults.ProxyPlugins
.SelectMany(p => p.GetCommands())
.ToList()
.ForEach(rootCommand.AddCommand);

rootCommand.Handler = proxyHost.GetCommandHandler(pluginEvents, [.. options], loaderResults.UrlsToWatch, logger);

// filter args to retrieve options
var incomingOptions = args.Where(arg => arg.StartsWith('-')).ToArray();

// remove the global options from the incoming options
incomingOptions = incomingOptions.Except(globalOptions).ToArray();

// compare the incoming options against the root command options
foreach (var option in rootCommand.Options)
{
// get the option aliases
var aliases = option.Aliases.ToArray();

// iterate over aliases
foreach (string alias in aliases)
{
// if the alias is present
if (incomingOptions.Contains(alias))
{
// remove the option from the incoming options
incomingOptions = incomingOptions.Where(val => val != alias).ToArray();
}
}
}

// list the remaining incoming options as unknown in the output
if (incomingOptions.Length > 0)
{
logger.LogError("Unknown option(s): {unknownOptions}", string.Join(" ", incomingOptions));
logger.LogInformation("TIP: Use --help view available options");
logger.LogInformation("TIP: Are you missing a plugin? See: https://aka.ms/devproxy/plugins");
}
else
{
await rootCommand.InvokeAsync(args);
}
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using Microsoft.DevProxy;
using Microsoft.DevProxy.Abstractions;
using Microsoft.DevProxy.Abstractions.LanguageModel;
using Microsoft.DevProxy.CommandHandlers;
using Microsoft.DevProxy.Logging;
using Microsoft.Extensions.Logging.Console;
using System.CommandLine;

_ = Announcement.ShowAsync();

PluginEvents pluginEvents = new();

ILogger BuildLogger()
{
var loggerFactory = LoggerFactory.Create(builder =>
{
builder
.AddConsole(options =>
{
options.FormatterName = "devproxy";
options.LogToStandardErrorThreshold = LogLevel.Warning;
})
.AddConsoleFormatter<ProxyConsoleFormatter, ConsoleFormatterOptions>(options => {
options.IncludeScopes = true;
})
.AddRequestLogger(pluginEvents)
.SetMinimumLevel(ProxyHost.LogLevel ?? ProxyCommandHandler.Configuration.LogLevel);
});
return loggerFactory.CreateLogger("devproxy");
}

var logger = BuildLogger();

var lmClient = new OllamaLanguageModelClient(ProxyCommandHandler.Configuration.LanguageModel, logger);
IProxyContext context = new ProxyContext(ProxyCommandHandler.Configuration, ProxyEngine.Certificate, lmClient);
ProxyHost proxyHost = new();

// this is where the root command is created which contains all commands and subcommands
RootCommand rootCommand = proxyHost.GetRootCommand(logger);

// store the global options that are created automatically for us
// rootCommand doesn't return the global options, so we have to store them manually
string[] globalOptions = ["--version", "--help", "-h", "/h", "-?", "/?"];

// check if any of the global options are present
var hasGlobalOption = args.Any(arg => globalOptions.Contains(arg));

// get the list of available subcommands
var subCommands = rootCommand.Children.OfType<Command>().Select(c => c.Name).ToArray();

// check if any of the subcommands are present
var hasSubCommand = args.Any(arg => subCommands.Contains(arg));

if (hasGlobalOption || hasSubCommand)
{
// we don't need to load plugins if the user is using a global option or using a subcommand, so we can exit early
await rootCommand.InvokeAsync(args);
return;
}

var pluginLoader = new PluginLoader(logger);
PluginLoaderResult loaderResults = await pluginLoader.LoadPluginsAsync(pluginEvents, context);
// have all the plugins init
pluginEvents.RaiseInit(new InitArgs());

var options = loaderResults.ProxyPlugins
.SelectMany(p => p.GetOptions())
// remove duplicates by comparing the option names
.GroupBy(o => o.Name)
.Select(g => g.First())
.ToList();
options.ForEach(rootCommand.AddOption);
// register all plugin commands
loaderResults.ProxyPlugins
.SelectMany(p => p.GetCommands())
.ToList()
.ForEach(rootCommand.AddCommand);

rootCommand.Handler = proxyHost.GetCommandHandler(pluginEvents, [.. options], loaderResults.UrlsToWatch, logger);

// filter args to retrieve options
var incomingOptions = args.Where(arg => arg.StartsWith('-')).ToArray();

// remove the global options from the incoming options
incomingOptions = incomingOptions.Except(globalOptions).ToArray();

// compare the incoming options against the root command options
foreach (var option in rootCommand.Options)
{
// get the option aliases
var aliases = option.Aliases.ToArray();

// iterate over aliases
foreach (string alias in aliases)
{
// if the alias is present
if (incomingOptions.Contains(alias))
{
// remove the option from the incoming options
incomingOptions = incomingOptions.Where(val => val != alias).ToArray();
}
}
}

// list the remaining incoming options as unknown in the output
if (incomingOptions.Length > 0)
{
logger.LogError("Unknown option(s): {unknownOptions}", string.Join(" ", incomingOptions));
logger.LogInformation("TIP: Use --help view available options");
logger.LogInformation("TIP: Are you missing a plugin? See: https://aka.ms/devproxy/plugins");
}
else
{
await rootCommand.InvokeAsync(args);
}