github / github/copilot-sdk

Add programmatic device-code login to CopilotClient

Đang mở
#1,109 0 bình luận 1 reaction 0 người được giao Xem trên GitHub
Ngôn ngữ chính
Java
Star
10.5k
Fork
1.5k
Merge trung bình
1 ngày 11 giờ
Pull request đã merge (30 ngày)
128

Mô tả

### 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.

Hướng dẫn đóng góp

Mở hướng dẫn đóng góp

Đánh giá

Issue này chưa được đánh giá.

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.