Bundle
dsh-oauth-copilot
GitHub Copilot OAuth provider and model adapter for DeepSeek Harness
- Source
- FMDD61
- License
- MIT
- Updated
- Updated 2 days ago
Readme
# dsh-oauth-copilot
[](https://www.npmjs.com/package/dsh-oauth-copilot) · [GitHub](https://github.com/FMDD61/dsh-oauth-copilot)
GitHub Copilot sign-in and model route for [DeepSeek Harness](https://www.npmjs.com/package/@deepseek-ai/dsh).
Sign in to GitHub via the device-code flow, store the Copilot grant through the harness credential
records, and register a **github-copilot** LLM route backed by pi-ai's Copilot model catalog — all on
official dsh seams, with no third-party OAuth base required.
> **Status: 0.1.1** — built and verified against dsh 0.1.1-rc.2 / pi-ai 0.82.1, and re-verified against dsh 0.1.2-alpha.1 (typecheck/build/13 tests, seams present). 中文版见
> [README.zh-CN.md](README.zh-CN.md).
## Why not the community dsh-oauth base
The current dsh-oauth release (0.2.0) is built against the 0.1.0-rc.6/rc.8 package line and its peers
are semver-incompatible with the shipped dsh 0.1.1-rc.2 — npm refuses to install both in one profile.
This plugin therefore targets the official seams directly (`ctx.credentials` records, `ctx.authorization`
flows, `ctx.llm` adapters), which also keeps the login working when dsh-llm-pi-ai's registered Copilot
flow is present.
## Install
Published form (no clone or build needed):
```sh
dsh plugin --profile web add dsh-oauth-copilot
```
Restart `dsh web`. The package's `cordis.patch.yml` (`llm-github-copilot`) is loaded from
`dsh.bundle.patch`; the `authorization` service entry in the profile patch activates the login
machinery (see Compatibility note).
Development form (from a checkout of this repo):
```sh
npm install && npm run build
dsh plugin --profile web add /path/to/dsh-oauth-copilot
```
## Use — manual CLI is the primary path
Sign-in is a **human-only terminal operation**; no LLM involvement, so prompt injection in a
non-official model cannot start or drop an authorization.
```sh
# The CLI ships with the npm package. Either install it globally...
npm install -g dsh-oauth-copilot
dsh-copilot-auth login # prints device-code URL + code; authorize in the browser
# ...or run it through npx without installing anything:
npx -y dsh-oauth-copilot login
dsh-copilot-auth status # grant expiry + available models
dsh-copilot-auth refresh # re-fetch the account-enabled model list into the grant
dsh-copilot-auth logout # remove the local grant
```
Only github.com is supported (GitHub Enterprise is not; see Known limitations). Tokens are never
printed; provider errors are redacted; the credential file is written atomically with 0600 perms.
## Model picker notes
The picker lists the advisory pi-ai catalog (29 Copilot models). Not every model is enabled for your
account: an unavailable pick returns HTTP 400 "model not supported". `dsh-copilot-auth refresh`
stores the account-enabled list (`policy=enabled`) into the grant, so the picker then shows only
those; a failed/empty refresh leaves the full advisory list visible — retry `refresh` or pick a model
from the `status` output.
## Opt-in model tools
Model-executable tools (`github_copilot_login` / `github_copilot_status` / `github_copilot_logout`)
are **off by default** and only register when the `authorization` service is mounted **and**
`enableModelTools: true` is set. Tool errors are redacted before they reach the model context.
```yaml
- id: llm-github-copilot
config:
enableModelTools: true # opt in; default false
loginWindowMs: 180000 # how long a login tool call waits before cancelling
streamIdleTimeoutMs: 300000
```
## Known limitations
- **GitHub Enterprise is not supported.** The grant whitelist accepts only the official endpoint
(`proxy.individual.githubcopilot.com`), and the model login tool and the CLI reject enterprise
domains explicitly. This is a security decision: a forged `enterpriseUrl` would redirect pi-ai's
automatic token refresh to an attacker server.
- **Tied to the 0.1.1-rc.2 package line.** Peer ranges are `^0.1.1-rc.2`; a future dsh 0.1.1 stable
or 0.2.0 upgrade will extend them along with the adapter contract.
- The CLI prints zh-CN messages (bilingual PRs welcome).
- Do not run `dsh-copilot-auth login/logout` while another process is actively writing the
credentials file; the CLI uses atomic 0600 writes but does not take dsh's cross-process write lock.
## FAQ
- **`dsh-copilot-auth: command not found`** — install the package with npm
(`npm install -g dsh-oauth-copilot`) or invoke `npx -y dsh-oauth-copilot login`.
- **Model request returns HTTP 400 "model not supported"** — run `dsh-copilot-auth refresh`, then
pick a model from the `status` output.
- **How do I revoke access completely?** `dsh-copilot-auth logout` removes the local grant; on
GitHub, Settings → Applications → revoke the Copilot authorization.
- **Where is the login button in the web UI?** There isn't one (dsh 0.1.1-rc.2 exposes no OAuth
surface in the Models page); the CLI is the sign-in path.
- **How do I update the plugin?** `npm install -g dsh-oauth-copilot@latest && dsh plugin --profile
web add dsh-oauth-copilot` (or bump the version in the profile package.json), then restart dsh web.
## Security posture
Reviewed against the community implementations (dsh-oauth / dsh-oauth-openai) with adversarial
review passes; decisions in place:
- **Grant validation** (`src/credential-store.ts`, CLI): stored payloads are schema-checked; the
`proxy-ep` endpoint must be the official host ONLY, and any non-empty `enterpriseUrl` rejects the
record — closing both the traffic-redirection and the refresh-token exfiltration paths.
- **Device flow** stays on github.com; no redirect URI, no local callback port, scope fixed at
`read:user`, one authorization attempt per key.
- **Redaction**: provider errors are JWT/token-pattern redacted and truncated before display.
- **Model-side authority**: sign-in/out tools default off; the CLI path needs no model at all.
- Credentials are stored 0600, outside settings/describe/environment, never echoed by the CLI.
## How it works
| Piece | File | Responsibility |
|---|---|---|
| Route | `src/adapter.ts` | Registers `github-copilot` via `PiAiAdapter`; request-level apiKey stays undefined so pi-ai's native OAuth auth takes over |
| Grant bridge | `src/credential-store.ts` | Strictly validated mapping of the harness record `llm-pi-ai/github-copilot` (kind `grant`) to a pi-ai `CredentialStore` + ambient `AuthContext` |
| Manual CLI | `scripts/dsh-copilot-auth.mjs` | Human-driven device-code login/status/refresh/logout writing the same record |
| Login tools | `src/login-tool.ts` | Optional model tools driving the `ctx.authorization` flow (opt-in only) |
## Development
```sh
npm install
npm run typecheck
npm test # unit + composition + grant validation
npm run build
# scripts/ ship with the repo, not the npm tarball: run them from a checkout
node scripts/smoke.mjs # mounts the built plugin on real seams in a temp DSH_HOME
node scripts/smoke-noauth.mjs # verifies the tree still boots without the authorization service
node scripts/verify-live.mjs # real-grant end-to-end probe (uses the real grant; sends one tiny request)
node scripts/verify-vision.mjs # opencode-go vision route probe (REASONING_EFFORT=low|high|max|off)
```
## Compatibility note
The model route always activates; login tools need the `authorization` service mounted and the
opt-in flag. dsh 0.1.1-rc.2's dsh-base does not mount that service by default — the profile patch
adds it. A missing service must never take the whole plugin tree down.
## License
MITInstall
dsh plugin --profile web add github:FMDD61/dsh-oauth-copilot
Profile: web
With the hub plugin installed, ask your agent to install it by name — it resolves the same plan shown here.
dsh plugin --profile web add github:stvlynn/dsh.fish#path:packages/dsh-plugin-hub
install dsh-oauth-copilot from the hub
- This package builds from source on install. pnpm will ask you to allow its build script — that is permission to run the package’s code on your machine, outside the agent sandbox. Only allow sources you trust.
- This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.