Repository navigation
feat: add dsh (DeepSeek Harness) CLI backend - #3
Merged
Merged
Conversation
Add dsh as a fourth CLI backend (agy/grok/codex/dsh). Runs `dsh --profile headless <prompt>` in the per-user session directory, extracts file:/// and markdown file links as send-back artifacts, and maps CLI errors to the standard Chinese notice bubbles. The headless profile always creates a fresh session per invocation, so the dsh backend is single-turn: /clear and /new are accepted but are no-ops, and model/effort/persona slash commands are not wired yet. - config.py: DSH_BIN_PATH / DSH_PROFILE / DSH_TIMEOUT / WECHATBRIDGE_DSH_HOME + backend whitelist - runner_common.py: register dsh in KNOWN_BACKENDS; .dsh cleanup paths - main.py: dispatch in _run_llm/_handle_slash/send_artifacts_back; /backend usage text now lists dsh - docs: README (en/zh), deploy env example, CHANGELOG - tests: tests/fake_dsh.py + tests/test_dsh.py (35 tests, all pass)
Security and correctness hardening for the dsh CLI backend: - Pass prompt as positional after -- to block option injection; fake_dsh stub now parses argv POSIX-style, with red/green tests - Strip bare file:/// links from display text; path extraction uses a whitelist so adjacent CJK text is not swallowed - Filter .dsh internal artifacts from send-back; clear .initialized.dsh on /clear - Machine-level session cleanup under DSH_HOME/sessions gated on explicit WECHATBRIDGE_DSH_HOME; unified strip+abspath normalization shared by runtime and cleanup; warn-once when unset - Replace @path mentions outside session_dir with [blocked-path]; token-based detection covers CJK, relative, symlink and adjacent-character bypass paths - Docs: -- in command examples, DSH_HOME semantics, attachment limits for dsh - Tests: 360 passing (baseline 325)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds dsh (DeepSeek Harness CLI) as a fourth CLI backend alongside agy / grok / codex, so WeChat messages can be routed to
dsh --profile headless <prompt>via/backend dsh(orWECHATBRIDGE_BACKEND=dsh).The headless profile answers one task, prints the final assistant message to stdout, writes
dsh: <code>: <message>to stderr on error, and exits 0 only on a completed turn (verified against the published@deepseek-ai/dsh-headlessbundle source).What's included
wechatbridge/dsh.py— new backend:run_dsh()(single-turn subprocess with per-user workspace isolation),extract_artifacts()(markdownfile:///links + absolute/relative file links, deduped by path),handle_dsh_slash_command()(/help,/clear//newas single-turn no-ops, TUI/dangerous command guards, passthrough otherwise).config.py—DSH_BIN_PATH/DSH_PROFILE(defaultheadless) /DSH_TIMEOUT/WECHATBRIDGE_DSH_HOME; backend whitelist includesdsh.runner_common.py—KNOWN_BACKENDSregistersdsh(per-user prefs get a dsh slot);.dsh/logs|cache|sessionsadded to retention cleanup.main.py— dispatch in_run_llm/_handle_slash/send_artifacts_back(dsh allowed root = per-user session dir);/backendusage text is now generated fromKNOWN_BACKENDS.deploy/wechatbridge.env.example, CHANGELOG entry.Design decisions
dsh-headlessalways creates a freshsession-<uuid>per invocation — there is no resume. Every WeChat message starts a new dsh session;/clear//neware accepted but are no-ops./model,/fast,/planning,/persona,/add-dirreturn a short "not supported on dsh" notice for v1.HOMEpointed at the per-user session dir, so the bridge always passesDSH_HOMEexplicitly (default host~/.dsh; override withWECHATBRIDGE_DSH_HOME).DSH_SESSION_ID/DSH_SESSION_JSONL/DSH_SHELLfrom the child env so a bridge process running inside a dsh session never leaks its own session vars.Tests
tests/test_dsh.py(35 tests) +tests/fake_dsh.py(fake headless CLI): command construction, artifact extraction (file:///, relative, dedup, URL-encoded), success/failure/timeout/empty paths, missing-credentials preflight, env assertions (DSH_HOME,HOME,DSH_SESSION_*stripping, cwd), slash commands, and/backend dshpreference switching. All pass./var→/private/varsymlink in codex/hardening path assertions) and reproduce onmainwithout this change.Acceptance needed
The dsh backend is implemented from the published headless bundle contract and a fake CLI in tests; final acceptance needs a real
dsh login+dsh --profile headlessrun on hardware (same status as the codex backend).