Skip to content

Latest commit

 

History

History
126 lines (124 loc) · 10.8 KB

File metadata and controls

126 lines (124 loc) · 10.8 KB

DSCodex install instructions for coding agents

This repository adds DeepSeek V4 Flash and Pro to the stock ChatGPT / Codex app through a loopback router. If a user asks you to install it, follow the "Install by a coding agent" section in README.md. The non-negotiable details:

  1. Never print or commit the DeepSeek API key. Persist it with node src/cli.mjs key set (DEEPSEEK_API_KEY env or the hidden prompt); it is stored at ~/.codex/dscodex/config.json with mode 0600 (DPAPI-encrypted on Windows) and survives logout/reboot. Resolution order at runtime: DEEPSEEK_API_KEY env (one-off override), then the stored file, then the legacy macOS login-session value. Never store the key in ~/.codex/config.toml; uninstall deletes the stored key file.
  2. Run node src/cli.mjs install, then node src/cli.mjs start, then node src/cli.mjs doctor. Doctor must report ok for config, catalog, router token, proxy, key, and the app-server bridge state. The app-server bridge is opt-in (node src/cli.mjs bridge enable) because a global CODEX_CLI_PATH demotes the app from its local daemon websocket (which supports reconnect) to stdio and breaks Computer Use; install therefore never sets it and actively removes DSCodex-owned copies left by older versions, including any CODEX_CLI_PATH the Codex app snapshotted into [mcp_servers.*.env]. When enabled, bridge enable must refuse a user-owned CODEX_CLI_PATH; the variable must point at the generated shim ~/.codex/dscodex/codex-cli-bridge.sh, never directly at src/codex-wrapper.mjs: GUI apps get a bare launchd PATH without Homebrew, so a #!/usr/bin/env node shebang fails there. The shim resolves node from PATH at runtime and only falls back to the absolute path baked at install time. The wrapper must resolve the stock Codex binary through the shared fallback chain (src/real-codex.mjs) instead of exiting when DSCODEX_REAL_CODEX is missing: launchctl login variables do not survive reboots, and a stale CODEX_CLI_PATH must degrade to stock Codex, not hard-fail every spawn. The bridge is macOS-only: Windows desktop apps spawn CODEX_CLI_PATH directly and cannot run a script shim (CreateProcess requires an .exe), so on Windows the bridge is unavailable and the doctor bridge check passes trivially.
  3. Run npm test; all tests must pass.
  4. The ChatGPT desktop app must be fully quit (⌘Q) and relaunched, and the user must start a NEW task to see 🐳 V4 Flash and 🐳 V4 Pro. Existing tasks keep their old model state.
  5. Verify each model with a real tool loop using codex -m deepseek/deepseek-v4-flash -c 'model_reasoning_effort="max"' -a never exec --skip-git-repo-check 'call a shell tool exactly once …' and the same command with deepseek/deepseek-v4-pro.
  6. Do not edit ~/.codex/config.toml by hand unless the user asks; the CLI owns its two marker-owned root keys. GUI-written model / model_reasoning_effort lines are user-owned and must be preserved.
  7. Provider selection memory lives in ~/.codex/dscodex/model-selections.json. OpenAI and DeepSeek have separate reasoning-effort slots; only OpenAI owns the saved service tier. The file also persists per-thread provider memory (bounded, last 500 threads) so resumed threads switch like live ones, and a staleEffort marker so a model-only config write never leaks the other provider's effort into a new session. On load, the bridge must reconcile its remembered active provider with the live config.toml model (the GUI keeps switching models while the bridge is off) and adopt that config's effort/tier into the matching slots; otherwise the next cross-provider switch is misclassified as an in-provider effort change and the carried effort overwrites the family slot instead of restoring it. The wrapper must forward all other app-server JSONL RPC unchanged to the stock Codex binary.
  8. DeepSeek V4 Flash and Pro are text-only. The router rewrites input_image parts in DeepSeek-bound bodies (both message content and view_image function_call_output results) into text descriptions generated by GPT through chatgpt.com using the incoming request's OAuth headers (default model gpt-5.6-sol, override with DSCODEX_VISION_MODEL; sha256-cached per router process; concurrent per request). Without an authorization header the body passes through untouched. Both DeepSeek catalog entries must keep input_modalities = ["text", "image"] or the desktop view_image gate rejects calls before images ever reach the router. Never route images to api.deepseek.com unchanged expecting vision — DeepSeek accepts the parts but cannot see them.
  9. Autostart is opt-in: node src/cli.mjs autostart enable (launchd com.dscodex.router on macOS, systemd user service dscodex.service on Linux, Task Scheduler DSCodex plus a hidden wscript shim on Windows). The generated plist/unit/VBS must never embed the DeepSeek API key — the router resolves it from the stored key file at runtime. KeepAlive/Restart only cover crashes: the Windows VBS must wait for a hidden supervisor that restarts nonzero router exits, with Task Scheduler restart settings as a second fallback; registration must finish before replacing a healthy manual router, and a failed handoff must restore that router. stop uses the authenticated shutdown endpoint, the router exits 0 gracefully, and it must stay down. The serve process owns ~/.codex/dscodex/server.pid no matter who launched it, so stop works for autostarted instances too. uninstall must disable autostart and delete the generated artifacts.
  10. The router must reach chatgpt.com for GPT passthrough and GPT vision. Node's fetch ignores proxy environment variables by default, so DSCodex resolves a proxy itself — order: DSCODEX_HTTPS_PROXY / DSCODEX_HTTP_PROXY, then standard proxy variables (Node gives lowercase names precedence), then the stored proxy_url written by node src/cli.mjs proxy set <url> in ~/.codex/dscodex/config.json — and re-execs itself with Node's --use-env-proxy (requires Node >= 24.5; uppercase and lowercase proxy variables are synchronized, and NO_PROXY always includes loopback plus api.deepseek.com). Proxy credentials are redacted in CLI output and DPAPI-protected on Windows; the proxy URL must never be confused with the DeepSeek key, which stays DPAPI/0600-protected and is never printed or committed.
  11. install generates a 256-bit router token and writes it into the managed openai_base_url; start / serve must reconcile that marker-owned URL with the persisted token and selected port, and doctor must verify the exact binding. The proxy must reject requests without that path token. serve owns a 0600 pid-state file with a per-instance shutdown token. stop may only use the authenticated shutdown endpoint and must atomically preserve replacement-instance state; it must never terminate an unverified or recycled PID. Cap both compressed request bytes and decompressed request bytes before parsing JSON.
  12. DeepSeek does not implement Codex remote compaction v2. For a DeepSeek-bound request containing compaction_trigger, the router must remove tools and the trigger, ask the same DeepSeek model for a compact handoff summary, and return exactly one synthetic compaction output item before response.completed. Encrypt the summary with AES-256-GCM using a key derived from the stable router token; on later DeepSeek requests, decrypt only DSCodex-prefixed compaction items and restore them as assistant summary context. A compaction item that cannot be decrypted (GPT-sealed after a provider switch, or a rotated token) must be dropped, never forwarded raw to DeepSeek. Never route compaction through GPT or store the summary as plaintext in the rollout file.
  13. DeepSeek's Responses API is stricter than OpenAI's about replaying tool-call turns, and a rejected replay wedges the session permanently because the bad shape stays in the history. Every tool output must directly follow its call — Codex inserts PostToolUse hook context as a developer message that can land inside the pair — so the router re-pairs them for function_call, custom_tool_call, and local_shell_call; the repair must preserve the relative order of every other item and must leave a call whose output is missing where it is. Pair on call_id, never on id: Codex sends both, but id is a local UUID and only call_id carries the call_… value the API matches on. A turn must never replay more than one tool call behind a single reasoning item (DeepSeek reports a misleading "reasoning_text must be passed back"), so the catalog entry sets supports_parallel_tool_calls = false and the router forces parallel_tool_calls: false; duplicating the turn's reasoning for extra calls is replay repair for already-wedged sessions only, and must never copy reasoning across a turn boundary. A turn runs from its reasoning item through its calls and includes the assistant preamble message Codex emits in between — real rollouts show reasoningmessage(assistant) → call, call — while a tool output ends it.
  14. Platform differences: routing, key storage, and catalog merging work identically on all platforms. The app-server bridge is macOS-only and opt-in — it demotes the app from its local daemon websocket (reconnect-capable) to stdio, which breaks Computer Use. install never sets CODEX_CLI_PATH and actively strips DSCodex-owned copies left by older versions, including copies the Codex app snapshotted into [mcp_servers.*.env]. The bridge shim (~/.codex/dscodex/codex-cli-bridge.sh) resolves node from PATH at runtime and falls back to the absolute path baked at install time; the wrapper (src/codex-wrapper.mjs) resolves the stock Codex binary through the shared fallback chain (src/real-codex.mjs) instead of hard-failing when DSCODEX_REAL_CODEX is missing (launchctl login variables do not survive reboots). On Windows, the bridge is unavailable (CreateProcess requires an .exe, cannot run a script shim), and doctor passes trivially. Windows config lives under %USERPROFILE%\\.codex; 0600 file permissions do not apply on NTFS (DSCodex relies on the user account ACL). Autostart uses the platform-native scheduler on all three OSes (launchd / systemd / Task Scheduler + VBS).
  15. Non-routed client features: Voice, Pets, plugins, skills, and MCP are all client-side and unaffected by the router. Voice is driven by GPT-Live and is never routed to DeepSeek. The catalog declares prefer_websockets = false — the router answers probes with 426, Codex falls back to HTTP/SSE, and codex doctor may show a warning but requests work fine.