< Summary

Information
Class: NexusLabs.Needlr.Copilot.GitHubOAuthTokenProvider
Assembly: NexusLabs.Needlr.Copilot
File(s): /home/runner/work/needlr/needlr/src/NexusLabs.Needlr.Copilot/GitHubOAuthTokenProvider.cs
Line coverage
29%
Covered lines: 16
Uncovered lines: 39
Coverable lines: 55
Total lines: 118
Line coverage: 29%
Branch coverage
23%
Covered branches: 9
Total branches: 38
Branch coverage: 23.6%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)50%22100%
GetOAuthToken()50%2275%
ResolveToken()7.14%1571410%
ReadFromAppsJson()0%110100%
ReadFromEnvironment()100%22100%
GetAppsJsonPath()50%14854.54%

File(s)

/home/runner/work/needlr/needlr/src/NexusLabs.Needlr.Copilot/GitHubOAuthTokenProvider.cs

#LineLine coverage
 1using System.Text.Json;
 2
 3namespace NexusLabs.Needlr.Copilot;
 4
 5/// <summary>
 6/// Default implementation that discovers the GitHub OAuth token from
 7/// configured sources (explicit value, apps.json, or environment variables).
 8/// </summary>
 9internal sealed class GitHubOAuthTokenProvider : IGitHubOAuthTokenProvider
 10{
 11    private readonly CopilotChatClientOptions _options;
 12    private string? _cachedToken;
 13
 1814    public GitHubOAuthTokenProvider(CopilotChatClientOptions options)
 15    {
 1816        _options = options ?? throw new ArgumentNullException(nameof(options));
 1817    }
 18
 19    public string GetOAuthToken()
 20    {
 1521        if (_cachedToken is not null)
 22        {
 023            return _cachedToken;
 24        }
 25
 1526        _cachedToken = ResolveToken();
 1527        return _cachedToken;
 28    }
 29
 30    private string ResolveToken()
 31    {
 1532        if (!string.IsNullOrWhiteSpace(_options.GitHubToken))
 33        {
 1534            return _options.GitHubToken;
 35        }
 36
 037        return _options.TokenSource switch
 038        {
 039            CopilotTokenSource.AppsJson => ReadFromAppsJson()
 040                ?? throw new InvalidOperationException(
 041                    "No OAuth token found in apps.json. Ensure the Copilot CLI is logged in."),
 042
 043            CopilotTokenSource.EnvironmentVariable => ReadFromEnvironment()
 044                ?? throw new InvalidOperationException(
 045                    "No GH_TOKEN or GITHUB_TOKEN environment variable found."),
 046
 047            CopilotTokenSource.Auto => ReadFromAppsJson()
 048                ?? ReadFromEnvironment()
 049                ?? throw new InvalidOperationException(
 050                    "No GitHub OAuth token found. Log in via the Copilot CLI, " +
 051                    "set GH_TOKEN/GITHUB_TOKEN, or provide an explicit GitHubToken in options."),
 052
 053            _ => throw new ArgumentOutOfRangeException(nameof(_options.TokenSource)),
 054        };
 55    }
 56
 57    internal static string? ReadFromAppsJson()
 58    {
 059        var appsJsonPath = GetAppsJsonPath();
 060        if (appsJsonPath is null || !File.Exists(appsJsonPath))
 61        {
 062            return null;
 63        }
 64
 65        try
 66        {
 067            var json = File.ReadAllText(appsJsonPath);
 068            using var doc = JsonDocument.Parse(json);
 69
 070            foreach (var property in doc.RootElement.EnumerateObject())
 71            {
 072                if (property.Value.TryGetProperty("oauth_token", out var tokenElement))
 73                {
 074                    var token = tokenElement.GetString();
 075                    if (!string.IsNullOrWhiteSpace(token))
 76                    {
 077                        return token;
 78                    }
 79                }
 80            }
 081        }
 082        catch (JsonException)
 83        {
 084        }
 85
 086        return null;
 087    }
 88
 89    internal static string? ReadFromEnvironment()
 90    {
 291        return Environment.GetEnvironmentVariable("GH_TOKEN")
 292            ?? Environment.GetEnvironmentVariable("GITHUB_TOKEN");
 93    }
 94
 95    internal static string? GetAppsJsonPath()
 96    {
 197        if (OperatingSystem.IsWindows())
 98        {
 099            var localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
 0100            return Path.Combine(localAppData, "github-copilot", "apps.json");
 101        }
 102
 1103        if (OperatingSystem.IsMacOS())
 104        {
 0105            var home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
 0106            return Path.Combine(home, "Library", "Application Support", "github-copilot", "apps.json");
 107        }
 108
 1109        if (OperatingSystem.IsLinux())
 110        {
 1111            var configHome = Environment.GetEnvironmentVariable("XDG_CONFIG_HOME")
 1112                ?? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".config");
 1113            return Path.Combine(configHome, "github-copilot", "apps.json");
 114        }
 115
 0116        return null;
 117    }
 118}