Lostforest7/dsh-encoding ↗★ 0

dsh-encoding

Windows命令行乱码修复与编码转换工具 适合Windows用户,解决执行本地命令时因GBK等编码导致的乱码问题。

套件
dsh-encoding
相容性
待驗證
Harness 依賴範圍
>=0.1.0
Cordis 依賴範圍
^4.0.0
版本
0.1.0
授權
MIT
最近更新
2026年9月30日

安裝

$npx -p @deepseek-ai/dsh dsh plugin --profile web add github:Lostforest7/dsh-encoding

dsh-encoding

Correct-encoding command runner and mojibake recovery for DeepSeek Harness.

让 DeepSeek Harness 在 Windows 上不再乱码。

English / 中文


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:

ToolWhat it does
runSpawns its own child process, captures raw bytes of stdout/stderr, auto-detects UTF-8 / UTF-16LE / GBK and decodes them correctly.
decode_textRecovers 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

ParameterTypeRequiredDefaultDescription
commandstringyes—Command line to execute (Windows: cmd /d /s /c, POSIX: /bin/bash -c).
workdirstringno—Working directory for the child process.
timeoutMsnumberno120000Timeout; the whole process tree is killed on expiry.
encodingstringnoautoauto (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: run is an independent new tool. It does not have the built-in bash tool's sandbox, approvals, or background-task support, and it executes with the same permissions as the harness process.

Tool: decode_text

ParameterTypeRequiredDefaultDescription
textstringyes—The garbled text to recover.
fromstringnoautoHow to turn characters back into bytes: latin1 / utf-16le / auto.
tostringnoautoTarget 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=gbk is 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=gbk is fully supported.

30-second example

Built-in bash shows ÖÐÎÄ where 中文 was expected (GBK bytes read as latin1):

  1. Ask the agent to use decode_text with {"text": "ÖÐÎÄ"} → 中文 — done.
  2. Or re-run the command with correct decoding: use run with {"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/pwsh tools, ctx.shell, or dsh-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 encoding when 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-encoding was unoccupied at the time of writing; fall back to dsh-encoding-fix if taken).
  • npm publish --registry=https://registry.npmjs.org (China mirrors are read-only; always publish against the official registry).
  • Submit to the awesome-dsh-plugin list 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

参数类型必填默认说明
commandstring是—要执行的命令(Windows:cmd /d /s /c;POSIX:/bin/bash -c)。
workdirstring否—子进程工作目录。
timeoutMsnumber否120000超时毫秒数,到期后杀掉整个进程树。
encodingstring否autoauto 自动检测,或强制 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

参数类型必填默认说明
textstring是—要还原的乱码文本。
fromstring否auto字符还原成字节的方式:latin1 / utf-16le / auto。
tostring否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 读):

  1. 让 agent 调用 decode_text,参数 {"text": "ÖÐÎÄ"} → 得到 中文,完成。
  2. 或者重新执行命令并按正确编码解码:调用 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.