dsh-encoding
Correct-encoding command runner and mojibake recovery for DeepSeek Harness. 让 DeepSeek Harness 在 Windows 上不再乱码。 适合Windows用户,解决执行本地命令时因GBK等编码导致的乱码问题。
Install
npx -p @deepseek-ai/dsh dsh plugin --profile web add github:Lostforest7/dsh-encodingREADME
Read the full README ↗dsh-encoding
Correct-encoding command runner and mojibake recovery for DeepSeek Harness.
让 DeepSeek Harness 在 Windows 上不再乱码。
English
dsh-encoding is a thin tools plugin for DeepSeek Harness (dsh). On Chinese Windows, legacy CLIs, Git Bash/MSYS2 and PowerShell 5.1 often write GBK (cp936) or UTF-16LE bytes, which dsh's built-in bash/pwsh tools decode as UTF-8 and show as mojibake. The decoding happens inside the non-pluggable core service (dsh-subprocess-local), so this plugin does not try to patch it. Instead it adds two complementary tools:
| Tool | What it does |
|---|---|
run | Spawns its own child process, captures raw bytes of stdout/stderr, auto-detects UTF-8 / UTF-16LE / GBK and decodes them correctly. |
decode_text | Recovers already-garbled text (e.g. GBK bytes misread as latin1) back to correct Chinese. |
Install
After it is published to npm:
dsh plugin --profile add dsh-encoding
From a local checkout (this repo):
dsh plugin --profile add ./dsh-encoding
Verify the layer is active, then start:
dsh --profile --dump-config # shows a "# == dsh-encoding" layer
dsh --profile
Because the package is pure ESM JavaScript with no build step, dsh plugin add github:you/dsh-encoding (git install) also works without any prepare build authorization.
Tool: run
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
command | string | yes | — | Command line to execute (Windows: cmd /d /s /c, POSIX: /bin/bash -c). |
workdir | string | no | — | Working directory for the child process. |
timeoutMs | number | no | 120000 | Timeout; the whole process tree is killed on expiry. |
encoding | string | no | auto | auto (detect) or force utf-8 / utf-16le / gbk. |
Example output (GBK output on Windows):
中文
[stderr]
... (if any stderr)
[encoding: gbk/utf-8 (auto-detected)]
[exit code: 0]
The last line is always [exit code: N]. When the output contains U+FFFD (�), an explicit warning line is appended instead of silently pretending the decode was right.
Positioning, stated plainly:
runis an independent new tool. It does not have the built-inbashtool's sandbox, approvals, or background-task support, and it executes with the same permissions as the harness process.
Tool: decode_text
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
text | string | yes | — | The garbled text to recover. |
from | string | no | auto | How to turn characters back into bytes: latin1 / utf-16le / auto. |
to | string | no | auto | Target decoding: gbk / utf-16le / auto. |
The classic mojibake path is from=latin1, to=gbk: GBK bytes were misread as latin1 (one character per byte), so the tool re-encodes each character to one byte and decodes the bytes as GBK.
With auto, the direction is guessed: embedded NULs → latin1 → utf-16le; wide characters → utf-16le → gbk; everything else → the classic latin1 → gbk.
Irreversible cases are reported, never faked: if the input already contains U+FFFD (�) — i.e. the shell already destroyed the bytes with an earlier mis-decode — the result is ok: false with an explicit note.
Note:
from=gbkis rejected with a clear explanation. Node.js ships a GBK decoder (TextDecoder) but no GBK encoder, and this plugin is zero-dependency, so re-encoding characters back to GBK bytes is not possible.to=gbkis fully supported.
30-second example
Built-in bash shows ÖÐÎÄ where 中文 was expected (GBK bytes read as latin1):
- Ask the agent to use
decode_textwith{"text": "ÖÐÎÄ"}→中文— done. - Or re-run the command with correct decoding: use
runwith{"command": "chcp 936 >nul & echo 中文"}→ output中文plus[encoding: gbk (auto-detected)] [exit code: 0].
CLI smoke test (no DSH required)
node src/cli.js decode_text "ÖÐÎÄ" # -> 中文
node src/cli.js run "chcp 936 >nul & echo 中文" # GBK output, auto-decoded
node src/cli.js run "echo hello"
node src/cli.js detect D6D0CEC4 # -> detected: gbk / decoded: 中文
Development
Zero dependencies — none at runtime, none for development; tests use only Node built-ins (node:test, node:assert). No npm install, no DSH, no build step needed:
node --test test/
Architecture:
dsh-encoding/
├── package.json # type: module, dsh.bundle.patch -> cordis.patch.yml
├── cordis.patch.yml # inserts one loader row: id "encoding", name "dsh-encoding"
├── src/index.js # Cordis entry (dependency-free), registers run + decode_text
├── src/encoding.js # pure functions: detect / decode / mojibake recovery
├── src/exec.js # spawn wrapper + run formatting (spawnImpl injectable)
├── src/cli.js # manual smoke runner (no DSH required)
└── test/ # node:test suites, mock child_process.spawn
src/encoding.js and src/exec.js import nothing from @deepseek-ai/*. src/index.js is dependency-free too: it emits the registry-ready tool shape directly (the same {type:'object', properties, required} + output.schema/render form that defineTool compiles to). This is deliberate — when a plugin is installed as a local checkout (dsh plugin add ./path, a pnpm link), the package lives outside the profile tree, and Node's ESM resolver cannot reach the host's @deepseek-ai/* packages from the checkout directory (the import would die with ERR_MODULE_NOT_FOUND). Emitting the registry shape directly keeps the plugin working under npm, git, and local-link installs alike.
Known limitations
- Does not replace or fix the built-in
bash/pwshtools,ctx.shell, ordsh-subprocess-local— those are out of scope by design (version-fragile core work). This plugin is a supplement, and says so. - No sandbox / approval / background-task integration for
run. - Encoding detection is heuristic (UTF-8 validity, UTF-16LE NUL pattern, GBK high-byte ratio, BOM). Ambiguous cases exist — e.g. BOM-less pure-CJK UTF-16LE vs GBK — so force
encodingwhen you know the answer. - U+FFFD in the input is information already destroyed; recovery is impossible and is reported as such.
- Output is capped at 8 MiB per stream; on timeout the process tree is killed (Windows:
taskkill /T /F, POSIX: process-group SIGKILL).
Publishing checklist
- Set the GitHub repository topic
dsh-plugin. - Confirm the npm name is free right before publishing (
dsh-encodingwas unoccupied at the time of writing; fall back todsh-encoding-fixif taken). -
npm publish --registry=https://registry.npmjs.org(China mirrors are read-only; always publish against the official registry). - Submit to the
awesome-dsh-pluginlist and the dsh-plugin.org market.
中文
dsh-encoding 是 DeepSeek Harness(dsh)的一个薄工具插件。在中文 Windows 上,老式 CLI、Git Bash/MSYS2、PowerShell 5.1 经常输出 GBK(cp936)或 UTF-16LE 字节,被 dsh 内置的 bash/pwsh 工具按 UTF-8 解码后变成乱码;而解码发生在不可插拔的核心服务(dsh-subprocess-local)里。本插件不去修补内置 bash,而是新增两个补充工具:
| 工具 | 作用 |
|---|---|
run | 自己 spawn 子进程、按原始字节收 stdout/stderr,自动识别 UTF-8 / UTF-16LE / GBK 并正确解码。 |
decode_text | 把已经乱码的文本(如 GBK 字节被当 latin1 误读)还原成正确中文。 |
安装
发布到 npm 后:
dsh plugin --profile add dsh-encoding
从本地目录(本仓库)安装:
dsh plugin --profile add ./dsh-encoding
验证配置层已生效,然后启动:
dsh --profile --dump-config # 能看到 "# == dsh-encoding" 层
dsh --profile
本包是纯 ESM JavaScript、无构建步骤,因此 dsh plugin add github:you/dsh-encoding(git 安装)同样开箱即用,不需要 prepare 构建授权。
工具 run
| 参数 | 类型 | 必填 | 默认 | 说明 |
|---|---|---|---|---|
command | string | 是 | — | 要执行的命令(Windows:cmd /d /s /c;POSIX:/bin/bash -c)。 |
workdir | string | 否 | — | 子进程工作目录。 |
timeoutMs | number | 否 | 120000 | 超时毫秒数,到期后杀掉整个进程树。 |
encoding | string | 否 | auto | auto 自动检测,或强制 utf-8 / utf-16le / gbk。 |
输出示例(Windows 上的 GBK 输出):
中文
[stderr]
...(如有 stderr)
[encoding: gbk/utf-8 (auto-detected)]
[exit code: 0]
最后一行固定是 [exit code: N]。当输出含 U+FFFD(�)时,会附加明确的警告行,而不是假装解码正确。
定位如实说明:
run是一个独立的新工具,不带内置bash工具的沙箱、审批与后台任务能力,并以 dsh 进程自身的权限执行命令。
工具 decode_text
| 参数 | 类型 | 必填 | 默认 | 说明 |
|---|---|---|---|---|
text | string | 是 | — | 要还原的乱码文本。 |
from | string | 否 | auto | 字符还原成字节的方式:latin1 / utf-16le / auto。 |
to | string | 否 | auto | 目标解码:gbk / utf-16le / auto。 |
经典"乱码还原"路径是 from=latin1, to=gbk:GBK 字节被当 latin1 误读(一个字符一个字节),工具把每个字符还原成一个字节,再按 GBK 解码。
auto 模式下自动判断误读方向:文本含 NUL → latin1 → utf-16le;含宽字符 → utf-16le → gbk;其余 → 经典 latin1 → gbk。
不可逆情况明确说明、不伪造结果:若输入里已经含 U+FFFD(�)——即字节早已在上游误解码中被毁掉——返回 ok: false 并附说明。
注意:
from=gbk会被明确拒绝并解释原因——Node.js 内置了 GBK 解码器(TextDecoder)但没有 GBK 编码器,本插件又坚持零依赖,因此无法把字符重新编码回 GBK 字节。to=gbk完全支持。
30 秒示例
内置 bash 把 中文 显示成了 ÖÐÎÄ(GBK 字节被当 latin1 读):
- 让 agent 调用
decode_text,参数{"text": "ÖÐÎÄ"}→ 得到中文,完成。 - 或者重新执行命令并按正确编码解码:调用
run,参数{"command": "chcp 936 >nul & echo 中文"}→ 输出中文,并附[encoding: gbk (auto-detected)] [exit code: 0]。
CLI 手动冒烟(无需 DSH)
node src/cli.js decode_text "ÖÐÎÄ" # -> 中文
node src/cli.js run "chcp 936 >nul & echo 中文" # GBK 输出自动解码
node src/cli.js run "echo hello"
node src/cli.js detect D6D0CEC4 # -> detected: gbk / decoded: 中文
本地测试
零依赖——运行时与开发时都没有任何依赖;测试只用 Node 内置的 node:test / node:assert,无需 npm install、无需 DSH、无需构建:
node --test test/
结构:
dsh-encoding/
├── package.json # type: module,dsh.bundle.patch -> cordis.patch.yml
├── cordis.patch.yml # 插入一行 loader:id "encoding",name "dsh-encoding"
├── src/index.js # Cordis 入口(零依赖),注册 run 与 decode_text
├── src/encoding.js # 纯函数:检测 / 解码 / 乱码还原
├── src/exec.js # spawn 封装 + run 输出组装(spawnImpl 可注入)
├── src/cli.js # 手动冒烟(无需 DSH)
└── test/ # node:test 套件,mock child_process.spawn
src/encoding.js 与 src/exec.js 不 import 任何 @deepseek-ai/*;src/index.js 同样零依赖:它直接输出注册表形态的工具定义(与 defineTool 编译产物一致的 {type:'object', properties, required} + output.schema/render)。这是刻意的——当插件以本地目录方式安装(dsh plugin add ./path,即 pnpm link)时,包位于 profile 树之外,Node 的 ESM 解析无法从插件目录找到宿主的 @deepseek-ai/* 包(会以 ERR_MODULE_NOT_FOUND 失败)。直接输出注册形态让插件在 npm、git、本地 link 三种安装方式下都能工作。
已知限制
- 不替换、不修复内置
bash/pwsh、ctx.shell、dsh-subprocess-local——刻意超出范围(核心层改动版本脆弱)。本插件定位为"补充工具"。 run无沙箱集成、无审批、无后台任务。- 编码检测是启发式的(UTF-8 合法性、UTF-16LE NUL 模式、GBK 高位字节占比、BOM)。存在歧义场景——例如无 BOM 的纯中文 UTF-16LE 与 GBK 难以区分——已知编码时请用
encoding参数强制指定。 - 输入中已有的 U+FFFD 属于已丢失的信息,无法还原,并会如实报告。
- 每路输出上限 8 MiB;超时杀进程树(Windows:
taskkill /T /F;POSIX:进程组 SIGKILL)。
发布清单
- 仓库添加 topic:
dsh-plugin。 - 发布前再次确认 npm 名未被占用(写作时
dsh-encoding未占用;若被抢注则退回dsh-encoding-fix)。 -
npm publish --registry=https://registry.npmjs.org(国内镜像只读,务必用官方 registry 发布)。 - 提交到
awesome-dsh-plugin列表与 dsh-plugin.org 市场。
License
MIT — see LICENSE.