From 5ed550b4aabdfa5ee76350fc0314bfc1a608ca41 Mon Sep 17 00:00:00 2001 From: Daniel Cazzulino Date: Thu, 1 May 2025 19:00:06 -0300 Subject: [PATCH] Add auth list command to view projects This makes it easier to view available tokens to use. Moved the hardcoded service URI which was everywhere to a project constant instead. --- src/dotnet-openai/App.cs | 2 +- src/dotnet-openai/Auth/AuthAppExtensions.cs | 1 + src/dotnet-openai/Auth/ListCommand.cs | 69 +++++++++++++++++++++ src/dotnet-openai/Auth/LoginCommand.cs | 6 +- src/dotnet-openai/Auth/LogoutCommand.cs | 4 +- src/dotnet-openai/Auth/StatusCommand.cs | 15 ++++- src/dotnet-openai/Auth/TokenCommand.cs | 2 +- src/dotnet-openai/Docs/auth.md | 2 + src/dotnet-openai/dotnet-openai.csproj | 3 +- 9 files changed, 94 insertions(+), 10 deletions(-) create mode 100644 src/dotnet-openai/Auth/ListCommand.cs diff --git a/src/dotnet-openai/App.cs b/src/dotnet-openai/App.cs index bdec7c7..97ac239 100644 --- a/src/dotnet-openai/App.cs +++ b/src/dotnet-openai/App.cs @@ -59,7 +59,7 @@ static CommandApp Create(IServiceCollection collection, [NotNull] out ITypeRegis var configuration = services.GetRequiredService(); var store = services.GetRequiredService(); - var apikey = store.Get("https://api.openai.com", "_CURRENT_")?.Password + var apikey = store.Get(ThisAssembly.Constants.ServiceUri, "_CURRENT_")?.Password ?? configuration["OPENAI_API_KEY"] ?? ""; diff --git a/src/dotnet-openai/Auth/AuthAppExtensions.cs b/src/dotnet-openai/Auth/AuthAppExtensions.cs index 91b45a2..fe05360 100644 --- a/src/dotnet-openai/Auth/AuthAppExtensions.cs +++ b/src/dotnet-openai/Auth/AuthAppExtensions.cs @@ -12,6 +12,7 @@ public static ICommandApp UseAuth(this ICommandApp app) { config.AddBranch("auth", group => { + group.AddCommand("list"); group.AddCommand("login"); group.AddCommand("logout"); group.AddCommand("status"); diff --git a/src/dotnet-openai/Auth/ListCommand.cs b/src/dotnet-openai/Auth/ListCommand.cs new file mode 100644 index 0000000..b9aa4e7 --- /dev/null +++ b/src/dotnet-openai/Auth/ListCommand.cs @@ -0,0 +1,69 @@ +using System.ComponentModel; +using GitCredentialManager; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Spectre.Console; +using Spectre.Console.Cli; + +namespace Devlooped.OpenAI.Auth; + +[Description("Lists projects that have been authenticated and can be used with the login command")] +[Service] +public class ListCommand(IAnsiConsole console, IConfiguration configuration, ICredentialStore store, CancellationTokenSource cts) : Command +{ + public override int Execute(CommandContext context, ListSettings settings) + { + var currentKey = store.Get(ThisAssembly.Constants.ServiceUri, "_CURRENT_")?.Password + ?? configuration["OPENAI_API_KEY"] + ?? ""; + + var projects = store + .GetAccounts(ThisAssembly.Constants.ServiceUri) + .Where(x => x != "_CURRENT_") + .Select(x => new { Project = x, Token = store.Get(ThisAssembly.Constants.ServiceUri, x)?.Password }); + + if (settings.Json) + { + if (settings.ShowToken) + return console.RenderJson(projects, settings, cts.Token); + else + return console.RenderJson(projects.Select(x => x.Project), settings, cts.Token); + } + + // render as a table otherwise + var table = new Table() + .Border(TableBorder.Rounded) + .AddColumn("[lime]Project[/]"); + + if (settings.ShowToken) + { + table.AddColumn("[lime]Token[/]"); + foreach (var project in projects) + { + table.AddRow([project.Token == currentKey ? $"[bold]{project.Project}[/]" : project.Project, project.Token ?? ""]); + } + } + else + { + foreach (var project in projects) + { + table.AddRow(project.Token == currentKey ? $"[bold]{project.Project}[/]" : project.Project); + } + } + + if (table.Rows.Count == 0) + console.MarkupLine("[red]No projects have been authenticated yet.[/]"); + else + console.Write(table); + + return 0; + } + + public class ListSettings : JsonCommandSettings + { + [Description("Display the auth token of each project")] + [DefaultValue(false)] + [CommandOption("--show-token")] + public bool ShowToken { get; set; } + } +} diff --git a/src/dotnet-openai/Auth/LoginCommand.cs b/src/dotnet-openai/Auth/LoginCommand.cs index 772e708..aa5c2fa 100644 --- a/src/dotnet-openai/Auth/LoginCommand.cs +++ b/src/dotnet-openai/Auth/LoginCommand.cs @@ -24,7 +24,7 @@ public class LoginCommand(IAnsiConsole console, ICredentialStore store) : Comman { public override int Execute(CommandContext context, LoginSettings settings) { - var token = store.Get("https://api.openai.com", settings.Project)?.Password; + var token = store.Get(ThisAssembly.Constants.ServiceUri, settings.Project)?.Password; if (settings.WithToken) { @@ -43,9 +43,9 @@ public override int Execute(CommandContext context, LoginSettings settings) return -2; } - store.AddOrUpdate("https://api.openai.com", settings.Project, token); + store.AddOrUpdate(ThisAssembly.Constants.ServiceUri, settings.Project, token); // Last login sets the current one. - store.AddOrUpdate("https://api.openai.com", "_CURRENT_", token); + store.AddOrUpdate(ThisAssembly.Constants.ServiceUri, "_CURRENT_", token); return 0; } diff --git a/src/dotnet-openai/Auth/LogoutCommand.cs b/src/dotnet-openai/Auth/LogoutCommand.cs index b01f9ed..b7904b7 100644 --- a/src/dotnet-openai/Auth/LogoutCommand.cs +++ b/src/dotnet-openai/Auth/LogoutCommand.cs @@ -12,9 +12,9 @@ public class LogoutCommand(ICredentialStore store, IAnsiConsole console) : Comma { public override int Execute(CommandContext context) { - foreach (var account in store.GetAccounts("https://api.openai.com")) + foreach (var account in store.GetAccounts(ThisAssembly.Constants.ServiceUri)) { - store.Remove("https://api.openai.com", account); + store.Remove(ThisAssembly.Constants.ServiceUri, account); if (account != "_CURRENT_") console.MarkupLine($" :check_mark: Logged out {account}"); } diff --git a/src/dotnet-openai/Auth/StatusCommand.cs b/src/dotnet-openai/Auth/StatusCommand.cs index 4c77edc..487a551 100644 --- a/src/dotnet-openai/Auth/StatusCommand.cs +++ b/src/dotnet-openai/Auth/StatusCommand.cs @@ -15,7 +15,7 @@ public class StatusCommand(IAnsiConsole console, IConfiguration configuration, I { public override async Task ExecuteAsync(CommandContext context, StatusSettings settings) { - var apikey = store.Get("https://api.openai.com", "_CURRENT_")?.Password + var apikey = store.Get(ThisAssembly.Constants.ServiceUri, "_CURRENT_")?.Password ?? configuration["OPENAI_API_KEY"] ?? ""; @@ -30,7 +30,18 @@ public override async Task ExecuteAsync(CommandContext context, StatusSetti { await client.GetOpenAIModelClient().GetModelsAsync(); - console.MarkupLine($" :check_mark_button: Logged in to api.openai.com"); + console.Markup($" :check_mark_button: Logged in to api.openai.com"); + if (store.GetAccounts(ThisAssembly.Constants.ServiceUri) + .Where(x => store.Get(ThisAssembly.Constants.ServiceUri, x)?.Password == apikey) + .FirstOrDefault() is { } project) + { + console.MarkupLine($" ([bold]{project}[/])"); + } + else + { + console.MarkupLine(" ([grey]OPENAI_API_KEY[/])"); + } + if (settings.ShowToken) console.MarkupLine($" :check_mark_button: Token: {apikey}"); diff --git a/src/dotnet-openai/Auth/TokenCommand.cs b/src/dotnet-openai/Auth/TokenCommand.cs index eeacaaf..571f91c 100644 --- a/src/dotnet-openai/Auth/TokenCommand.cs +++ b/src/dotnet-openai/Auth/TokenCommand.cs @@ -13,7 +13,7 @@ public class TokenCommand(IConfiguration configuration, ICredentialStore store, { public override int Execute(CommandContext context) { - var apikey = store.Get("https://api.openai.com", "_CURRENT_")?.Password + var apikey = store.Get(ThisAssembly.Constants.ServiceUri, "_CURRENT_")?.Password ?? configuration["OPENAI_API_KEY"] ?? ""; diff --git a/src/dotnet-openai/Docs/auth.md b/src/dotnet-openai/Docs/auth.md index 884d083..d4a85a0 100644 --- a/src/dotnet-openai/Docs/auth.md +++ b/src/dotnet-openai/Docs/auth.md @@ -7,6 +7,8 @@ OPTIONS: -h, --help Prints help information COMMANDS: + list Lists projects that have been authenticated and can be + used with the login command login Authenticate to OpenAI. Supports API key autentication using the Git Credential diff --git a/src/dotnet-openai/dotnet-openai.csproj b/src/dotnet-openai/dotnet-openai.csproj index 5e04248..70192de 100644 --- a/src/dotnet-openai/dotnet-openai.csproj +++ b/src/dotnet-openai/dotnet-openai.csproj @@ -1,4 +1,4 @@ - + Exe @@ -51,6 +51,7 @@ +