dsh-codex-subs-plugin
August 14, 2026 · View on GitHub
中文 | English
An experimental primary-model adapter for DeepSeek Harness that uses ChatGPT OAuth to access an account's Codex subscription capabilities instead of an OpenAI Platform API key.
Warning
OpenAI officially commits only that Codex can access subscriptions through "Sign in with ChatGPT." This project follows the open-source implementations of the OpenAI Codex client and OpenCode, and depends on compatibility surfaces not documented as stable, including the auth.openai.com device-code endpoints, ChatGPT-Account-Id, and chatgpt.com/backend-api/codex. Server-side changes may break compatibility at any time. A subscription does not provide unlimited usage.
For complete installation, login, default-model, proxy, Headless, and Web UI instructions, see the USAGE guide.
For implementation details, upstream source evidence, and risk analysis, see How OpenCode uses a Codex subscription.
How it works
ChatGPT OAuth (PKCE / device code)
→ access + refresh token + account id
→ translate DSH GenerateOptions into a Responses request
→ send only to chatgpt.com/backend-api/codex/responses
→ translate Responses SSE into DSH StreamChunk
→ preserve encrypted reasoning / tool replay state
It does not read or copy credentials from Codex CLI or OpenCode, and it does not "convert" a subscription into an API key. Its credentials are stored independently at, in precedence order:
$DSH_CODEX_SUBS_AUTH_FILE
or $DSH_HOME/codex-subs/auth.json
or ~/.dsh/codex-subs/auth.json
Writes use a temporary file, fsync, atomic rename, and 0600 permissions. Refresh starts 60 seconds before expiry and coalesces concurrent requests. The transport exposes no custom endpoint setting; the OAuth bearer can be sent only to the fixed Codex backend encoded in the source.
Quick start
Requires Node.js ^22.19.0 or >=24.0.0, pnpm, and an installed dsh CLI.
1. Install
pnpm install
pnpm run check
dsh plugin --profile headless add .
This package's bundle is non-invasive: it only registers the codex-subscription provider and does not replace DSH's existing default model. The following optional command can only confirm that the Cordis composition contains llm-codex-subscription:
dsh --profile headless --dump-config
--dump-config does not load the runtime model selection from $DSH_HOME/settings.yaml, so it cannot prove that requests will use this plugin.
2. Log in
dsh plugin --profile headless exec dsh-codex-subs login
On a machine without a desktop browser, use the time-limited device-code flow:
Device-code login is currently a beta feature. A personal account must first enable it under ChatGPT Settings → Security; for a managed workspace, an administrator must enable it in workspace permissions. See OpenAI authentication.
dsh plugin --profile headless exec dsh-codex-subs login --headless
3. Explicitly select the default model
Edit $DSH_HOME/settings.yaml; when DSH_HOME is unset, the default path is ~/.dsh/settings.yaml:
agent-default-model:
provider: codex-subscription
model: gpt-5.5
reasoningEffort: medium
The default catalog is only a current compatibility snapshot. Unlisted model IDs are still passed through unchanged, and actual model access is determined by the server.
4. Check local status
dsh plugin --profile headless exec dsh-codex-subs status
dsh plugin --profile headless exec dsh-codex-subs doctor
status reads local credential metadata. doctor neither refreshes the token nor makes a network request; it reports only whether auth exists or is expired, whether an account ID is present, profile installation state, the plugin/provider, the default provider/model declared in settings.yaml, whether proxy variables are present, the Node environment-proxy switch, and the fixed endpoint. It does not output tokens, the account ID, proxy URLs, or proxy-variable values. Neither command verifies remote entitlement or proves that DSH selected this route at runtime.
5. Complete a minimal request round trip
dsh --profile headless "Reply with exactly: codex-subscription-ok"
This is the current real runtime verification: a new headless agent reads the effective default model and sends a request through the selected adapter. A successful response jointly verifies runtime routing, OAuth, networking, and server-side access. It consumes subscription usage.
When you no longer use the plugin, you can delete its own local credential:
dsh plugin --profile headless exec dsh-codex-subs logout
logout does not remotely revoke the token or sign out of ChatGPT, Codex CLI, or OpenCode.
Proxy environment
OAuth, refresh, device-code, and Responses requests share one isolated, proxy-aware transport. It reads HTTP_PROXY, HTTPS_PROXY, and NO_PROXY, including their lowercase forms; when both cases of the same variable are present, lowercase takes precedence. Example:
export HTTPS_PROXY=http://proxy.example:8080
export HTTP_PROXY=http://proxy.example:8080
export NO_PROXY=localhost,127.0.0.1
- The current transport neither uses
ALL_PROXYnor counts it towarddoctor's proxy presence. SetHTTPS_PROXY/HTTP_PROXYas appropriate for the protocol. - When
HTTPS_PROXYis not set separately, Undici falls back toHTTP_PROXYfor HTTPS requests. Setting both explicitly makes diagnosis easier. - The authorization page opens in an external browser and uses the browser or operating system's network path. CLI token and model requests use this plugin's transport, so their egress paths may differ.
- Node 24 provides
NODE_USE_ENV_PROXY=1/--use-env-proxyas a global HTTP(S) client capability. This is diagnostic context only: on supported Node 22 and 24 versions, the plugin uses its own unified transport and does not depend on that switch. - The transport refuses to follow redirects automatically and redacts network errors. Even if a proxy URL contains credentials, never paste it or a complete environment-variable value into logs, issues, or chats.
Troubleshooting unsupported_country_region_territory
This error comes from the server. The plugin preserves the stable error code UNSUPPORTED_COUNTRY_REGION_TERRITORY, but it cannot override regional, account, or workspace policies:
- Run
statusanddoctorfirst to check auth, the default provider/model, and proxy-variable state. They do not test the remote service. - Compare against OpenAI's supported countries and territories. That page describes API service availability; the private ChatGPT Codex backend still makes the final eligibility decision.
- Ask the network or enterprise administrator to confirm the public egress country or region of the CLI process. A successful browser login does not mean the CLI request uses the same egress path.
- Check for conflicting or stale uppercase and lowercase proxy variables. This implementation gives lowercase precedence. Confirm that
NO_PROXYdoes not unintentionally bypass the proxy forauth.openai.comorchatgpt.com; setting onlyALL_PROXYhas no effect on this transport. - Retry the minimal request after establishing a consistent, compliant network path. Log in again if the failing stage is OAuth/device/refresh or the credential has expired. If it still fails, retain the failure stage, HTTP status, and request ID, remove tokens, complete request headers, and proxy URLs, then contact OpenAI Support or the workspace administrator.
Do not try to bypass server-side controls by forging a client ID, endpoint, copying someone else's credentials, or evading regional restrictions.
Plugin configuration
Override the row inserted by the bundle in the profile's cordis.patch.yml:
- id: llm-codex-subscription
config:
provider: codex-subscription
displayName: OpenAI Codex Subscription
reasoningEffort: medium
textVerbosity: low
streamIdleTimeoutMs: 300000
Other optional fields are an absolute authFile path and an advisory models list. There is no baseURL or endpoint setting; this is part of the token boundary.
Implemented
- Browser PKCE + state +
127.0.0.1-only callback, with a five-minute timeout and guaranteed cleanup. - Headless device flow with a 15-minute total timeout.
- Atomic persistence of refresh-token rotation, early refresh, and single-flight coordination.
- One redacting environment-proxy transport shared by OAuth, refresh, device, and Responses.
doctorsafely reports local auth, installation, the default model, and proxy presence without making a network request.- Forced refresh followed by one safe replay after the first 401, with no retry loop.
- Responses mappings for text, reasoning summary, function calls, usage, errors, and incomplete results.
- Stateless replay of encrypted reasoning and tool calls under
store:false. - DSH
usage → finishordering, abort, and stream idle-timeout contracts.
If you only need Codex as a subagent, prefer DeepSeek Harness's built-in @deepseek-ai/dsh-subagent-codex. It invokes the official codex app-server and uses native Codex authentication. This project is for connecting a Codex subscription to DSH as the primary LLM route.
Development verification
pnpm run typecheck
pnpm run test
pnpm run build
pnpm pack --dry-run
All tests use mock OAuth / Responses data. They do not read real credentials or access OpenAI.