Add programmatic device-code login to CopilotClient
- 主要语言
- Java
- 星标
- 10.5k
- 派生
- 1.5k
- 平均合并
- 1 天 11 小时
- 30 天内合并 PR
- 128
描述
### Problem
`CopilotClient` exposes `GetAuthStatusAsync` so callers can detect an unauthenticated state, but there is no SDK method to *transition* the session into an authenticated one. The only auth inputs today are:
- `CopilotClientOptions.GitHubToken`
- `GITHUB_TOKEN` / `GH_TOKEN` env vars
- Pre-existing cached credentials under `~/.copilot/` from a prior interactive `copilot /login` run
This works on machines where credentials have already been seeded — typical on Windows where the Copilot Chat extension or `gh copilot` may have populated the cache — but on a fresh macOS or Linux install there is no in-SDK path to bootstrap auth. The only options are:
1. Require the caller to supply a Personal Access Token. PATs are blocked by policy in many environments and can't be issued at all for some account types.
2. Launch the bundled CLI binary (`runtimes//native/copilot`) out-of-band and have the user run `/login` interactively, then re-enter the SDK once the credential file lands on disk.
Option 2 forces every SDK consumer to:
- Locate the bundled CLI binary across RIDs.
- Manage an external process lifecycle (cancellation, orphan cleanup, exit-code handling).
- Surrender stdio to the embedded CLI, which precludes custom UX, headless flows, and structured logging.
- Bypass the SDK's normal RPC contract entirely for this one capability.
### Proposal
Add a programmatic device-code OAuth flow to `CopilotClient` that yields the verification URL and user code via a callback (or an `IAsyncEnumerable` of state events) so consumers can render the prompt in their own UI:
```csharp
public Task LoginAsync(
LoginOptions options,
CancellationToken cancellationToken = default);
public sealed class LoginOptions
{
/// Invoked once with the device code + verification URL the user must visit.
public Action? OnDeviceCode { get; init; }
/// Optional override for the OAuth client / scopes if the SDK supports more than one tier.
public string? ClientId { get; init; }
}
public sealed record DeviceCodePrompt(
string UserCode,
Uri VerificationUri,
TimeSpan ExpiresIn,
TimeSpan PollInterval);
public sealed record AuthResult(
bool Success,
string? AccountLogin,
string? FailureReason);
```
After `LoginAsync` returns successfully, `GetAuthStatusAsync` should reflect the new state and subsequent RPC calls (`models.list`, `session.send`, etc.) should succeed without restarting the client.
A complementary `LogoutAsync()` that clears the cached credentials would round out the surface but is lower priority.
### Why this belongs in the SDK
- **Parity with `gh auth`, Octokit's device flow, and the VS Code Copilot extension** — all of which provide a programmatic device-code path. The SDK is the only Copilot client that requires shelling out to authenticate.
- **No-PAT bootstrap** — enables the SDK to be used in environments where PATs are unavailable or restricted, without forcing a separate CLI install.
- **Single source of truth for credential storage** — the SDK already reads `~/.copilot/`; centralizing the write path here prevents callers from drifting if the storage format changes.
- **Cancellable, awaitable, structured** — replaces an opaque interactive subprocess with a normal async API that cooperates with `CancellationToken`, structured logging, and host-defined UX.
- **Headless / CI scenarios** — a callback-based device flow can be driven by automation (forwarding the code to chat, email, a webhook, etc.); an interactive subprocess cannot.
### Alternatives considered
1. **Document the shell-out** — pushes the same complexity into every SDK consumer.
2. **Expose a `LoginUri` / `LoginPoll` pair** instead of a single `LoginAsync` — more flexible but pushes polling logic onto callers.
3. **Reuse `gh auth` tokens** — only works where `gh` is installed and authorized for the right account; not portable and still needs an external tool.
A first-class `LoginAsync` on `CopilotClient` is the smallest API addition that closes the gap.
贡献指南
调研方向
从 CopilotClient、GetAuthStatusAsync、CopilotClientOptions 以及现有的 ~/.copilot 凭据处理入手,梳理认证流程和 RPC 客户端状态。定义 LoginAsync、LoginOptions、DeviceCodePrompt、AuthResult 以及可选的 LogoutAsync 行为,然后验证成功登录会更新认证状态,并允许在不重启客户端的情况下使用 models.list 和 session.send。
由索引模型根据 Issue 内容生成。
评估
- 技术栈
- csharp
- 领域
- api, authentication
- Issue 类型
- 功能
- 难度
- 5/5
- 预计耗时
- 一周以上
- 活跃度
- 冷清
- 描述清晰度
- 基本清楚
- 新手友好度
- 35/100