dsh-plugin-chatgpt
Signs in to a ChatGPT account over OAuth and serves that plan's models as a DeepSeek Harness provider route, calling api.openai.com/v1/responses with the account's plan scope rather than the Codex backend.
Install
dsh plugin --profile web add dsh-plugin-chatgpt
Sandbox test results
| Stage | Result |
|---|---|
| 1 install | ✓ |
| 2 list | ✓ |
| 3 mount | ✓ |
| 4 boot | — |
Four stages run in an isolated sandbox: install (network on) → list → mount → boot (last three with no network).
README
dsh-plugin-chatgpt
English | [中文](docs/README.zh.md)
Adds one DeepSeek Harness provider route, chatgpt, that answers model calls from a signed-in ChatGPT subscription rather than an API key. Signing in uses OpenAI's published Sign in with ChatGPT flow, so the account's own plan pays for inference. The route is named chatgpt and not openai because the built-in pi-ai plugin already owns that name. Models, their context windows, and their reasoning levels come from your account's own listing, so the roster follows your plan rather than a hard-coded list.
Table of Contents
- [Use this package](#use-this-package)
- [What you get](#what-you-get)
- [Understand the implementation](#understand-the-implementation)
- [Further Exploration](#further-exploration)
- [Model Experience](#model-experience)
- [Known Limitations and Deferred Work](#known-limitations-and-deferred-work)
- [Dev Note](#dev-note)
<a id="use-this-package"></a>
Use this package
Install into a profile
The whole install is one line:
dsh plugin --profile <name> add dsh-plugin-chatgpt
Restart the harness afterwards: a profile is composed at startup. Removing it:
dsh plugin --profile <name> remove dsh-plugin-chatgpt
The package is on npm, so the registry is the whole install and no build step is involved:
dsh plugin --profile <name> add dsh-plugin-chatgpt@0.1.9 # pin a version, or omit it for the latest
Installing from a repository URL instead builds from source, and pnpm refuses a git dependency's build scripts until they are allowlisted: add https://github.com/ckanner/dsh-plugin-chatgpt fails with ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED until dsh-plugin-chatgpt is approved under onlyBuiltDependencies in the profile's pnpm-workspace.yaml.
The package declares dsh.bundle, so adding it appends the bundle to the profile's dsh.profile.bundles and inserts its row into the composition. Verify the layer without booting:
dsh --profile <name> --dump-config # shows a "# == dsh-plugin-chatgpt" layer
A package installing without a dsh.bundle declaration activates no layer; dsh plugin warns instead.
Sign in
Open Settings → Models. The plugin owns a ChatGPT row, and the card on that row offers Sign in with ChatGPT. The card shows the authorization URL and selects it for copying, accepts the final redirect URL pasted back when the browser cannot reach the loopback address, and afterwards reports the account — its plan, the token's next refresh, and how many models it offers — with a Sign out button. No token reaches the page.
Signing out revokes the renewable session at the authorization server before clearing it locally, and says so when the server did not confirm.
A headless harness drives the same session object with no browser at all:
import { ChatGptAuth } from 'dsh-plugin-chatgpt/src/auth/manager.ts'
const auth = new ChatGptAuth({
stateDir: `${process.env.DSH_HOME ?? `${process.env.HOME}/.dsh`}/chatgpt-subscription`,
agentNameHint: 'DeepSeek Harness',
})
const attempt = await auth.begin()
console.log('Open this URL in your browser:\n' + attempt.authorizationUrl)
// The browser redirects to the loopback listener this attempt bound. If the
// redirect cannot reach it — a headless host, or a blocked popup — paste the
// final URL back instead.
// await attempt.submit('<the full redirect URL from your browser>')
const credential = await attempt.result
auth.adopt(credential)
The grant is stored owner-only under stateDir. attempt.submit() rejects a malformed or mismatched value and leaves the attempt open, so a mistyped paste can be corrected.
<a id="what-you-get"></a>
What you get
One provider route, listed in Settings → Models like any built-in provider, with the model roster, context windows, and reasoning levels the account is entitled to. Selecting a model there is what puts it in the model selector; no separate picker is involved.
Model metadata comes from the account listing. By default the adapter follows each model's advertised context window, including updates when the catalog refreshes. Set contextWindowLimit for a global budget or modelContextWindowLimits for individual models; explicit values are used as-is. Sign-in lives on that row's card.
For example, add this field to the plugin's configuration to set DSH's context budget to 1,000,000 tokens for models without an individual override, even if the catalog reports a maximum of 872,000. Remove it to restore account defaults for those models. These settings are independent of Codex settings. DSH still reserves headroom for compaction; they do not change service-side request limits.
contextWindowLimit: 1000000
For separate model budgets, use exact model IDs as dictionary keys. The priority is individual model, then global budget, then account default. In this example Astra uses 1,000,000, Sol uses 800,000, and other models use 500,000. The global field is optional; omit it to let other models follow their account defaults.
contextWindowLimit: 500000 modelContextWindowLimits: gpt-6-astra: 1000000 gpt-6.1-sol: 800000
Changes take effect the next time DSH resolves a model. Removing a model entry restores the current global budget, or its account default when no global budget is set. Keys are case-sensitive exact IDs, not display names or wildcard patterns.
The share of the plan this installation may spend is set per app, in ChatGPT settings:
<a id="understand-the-implementation"></a
…(truncated)
Rendered from the README published on npm. Formatted only, not edited.
Versions
Published on npm::16,最后更新 2026-10-29