使用 dsh-bash-encoding 修復 DeepSeek Harness 的 bash 中文亂碼

前言

DeepSeek Harness(簡稱 DSH)是 DeepSeek 開源的智能體運行時,官方倉庫把核心理念寫成「Everything is a Plugin」(一切皆插件)。bash 工具是其中最常用的能力之一:模型下發一條命令,宿主把 stdout / stderr 收上來,再交給後續推理。

在 Linux 或 macOS 上,這條鏈路大多按 UTF-8 工作,問題不明顯。到了 Windows + WSL 組合裏,情況就不一樣了。DSH 跑在 Windows 側,bash 經 wsl.exe 執行;當 WSL 仍是 NAT 網絡、同時 HTTP_PROXY / HTTPS_PROXY 指向 localhost 時,啓動器會在 stderr 上打一條 UTF-16LE 編碼的代理警告。DSH 核心的 subprocess 層對所有輸出做 Buffer.toString('utf8'),原始字節在這一步丟掉,警告變成不可恢復的亂碼。更麻煩的是,這條警告經常和命令自己的 UTF-8 輸出擠在同一條管道里,按某一種編碼通解,會把另一段也解壞。

社區維護者 lhh010 做了 dsh-bash-encoding,專門替換 ctx.bash 執行器:自己 spawn、自己收集原始 Buffer、檢測後再解碼。它被收錄在社區插件目錄的「工具與能力」分類下。需要先說明:這個目錄站點(deepseek-harness-plugin.com)是獨立社區索引,和 DeepSeek / 幻方沒有官方從屬關係,安裝前仍要以倉庫源碼和許可證爲準。

這是什麼

dsh-bash-encoding 是一個宿主側 DSH 插件,npm 包名爲 @dsh-external/dsh-bash-encoding,當前版本 0.1.0,許可證 BSD-3-Clause,主要語言 TypeScript。GitHub 倉庫爲 lhh010/dsh-bash-encoding,截至 2026-08-18 星標 7。

它解決的問題很具體:自動識別 bash 輸出裏的 UTF-16LE / UTF-8 / GBK(以及 GB18030、UTF-16BE、帶 BOM 的變體),把 Web、TUI、hooks 橋和後臺任務裏看到的中文從亂碼還原成可讀文本。它不改 DSH 的 subprocess 核心服務,而是繞過那一層有損解碼——任何包在 ctx.subprocess 之上的包裝都修不了這個問題,因爲拿到的已經是亂碼文本。

倉庫 README 寫明兼容 DSH snapshot0808(snapshots/20260808T121140Z)、snapshot0809(snapshots/20260809T140917Z),以及 npm 發版 @deepseek-ai/dsh@0.0.1-rc.1。這些版本號以倉庫說明爲準;換 snapshot 前仍建議對照當前 DSH 的 bash 縫合線是否還叫 ctx.bash。官方文檔裏這條縫合線會拆到 dsh-bash-localdsh-bash-sandbox 等包,本插件替換的是其中的 ctx.bash 實現。

編碼檢測怎麼做

插件把執行器換成 EncodingBashExecutor:子進程由插件自己 spawn,stdout / stderr 先按原始字節收集,再交給 src/decode-core.ts 裏的流式分段解碼。檢測順序在 README 裏寫得很清楚:

  1. 純 ASCII 快速路徑:沒有高字節就按 UTF-8 處理,避免把 STDERRwsl: 這類字母誤判成 UTF-16。
  2. BOM:UTF-8(EF BB BF)、UTF-16LE(FF FE)、UTF-16BE(FE FF)。
  3. UTF-16 段檢測:按 chunk 分段。用 NUL 奇偶位錨定 ASCII 子段,用 CJK 高字節優勢錨定純中文段;連續 4 個可打印 ASCII code unit 會斷段(用來區分 STDERR 和「個」「片」這類字),遇到 UTF-8 三字節簽名則硬斷。
  4. 嚴格 UTF-8(fatal decoder)通過,則定爲 UTF-8。
  5. 再試 GBK,然後 GB18030(對應 Windows 中文 OEM 代碼頁 936 / 54936)。
  6. 最後 Latin-1 兜底,保證解碼不會失敗。

同一條管道里混着 WSL 的 UTF-16LE 警告和命令自己的 UTF-8 輸出,是這個檢測鏈要處理的難點。倉庫給出的對比大致是這樣。

修復前(核心 exec 按 UTF-8 解 UTF-16LE):

w s l: �hKm0R localhost �NtM�nFO*g\��P0R WSL0NAT !j_N�v WSL \rN/e c localhost �Nt

修復後:

wsl: 檢測到 localhost 代理配置,但未鏡像到 WSL。NAT 模式下的 WSL 不支持 localhost 代理。

GBK 工具的輸出、8KB 以上跨 chunk 的長 UTF-16 流,以及 STDERR: 這種 ASCII 前綴後跟中文的形態,README 裏都有對照示例。倉庫提供 26 個測試用例,覆蓋解碼內核和真實 spawn 的退出碼、stdin、超時、後臺任務、失敗路徑,用下面這條命令跑:

pnpm test

作者在 npm 發版基線上的實測記錄是 25/26 通過,唯一失敗被寫成「本機 WSL localhost 代理警告混入 stderr 的環境噪音」,解碼行爲本身仍被標記爲正確。

安裝與啓用

社區目錄頁給出的安裝命令是:

dsh plugin add github:lhh010/dsh-bash-encoding

目錄頁同時提示:如需可復現安裝,應固定 commit 哈希。當前 main 分支 HEAD 爲 30ecc056cbdade90292ff8ad72a2cd8324fe863f(提交於 2026-08-13),可以寫成:

dsh plugin add github:lhh010/dsh-bash-encoding#30ecc056cbdade90292ff8ad72a2cd8324fe863f

插件以當前 dsh 進程的權限運行,安裝時可能執行代碼。安裝前請檢查源代碼倉庫和許可證。

同一 context 裏只能有一個 ctx.bash 實現。接入後需要在 profile 的 cordis.yml(或 cordis.patch.yml)裏替換原來的 bash 條目,@deepseek-ai/dsh-bash-local@deepseek-ai/dsh-bash-sandbox 二選一被本插件替代,不能並存。倉庫給出的配置如下:

- id: bash
  name: '@dsh-external/dsh-bash-encoding'
  config:
    cwd: null            # 默認工作目錄(默認 process.cwd())
    timeoutMs: 120000    # 前臺命令默認超時
    maxTimeoutMs: 600000 # 單次超時上限
    maxOutputBytes: 65536  # 每流輸出上限(超限截斷並標記 lossy)
    graceMs: 3000        # SIGTERM→SIGKILL 寬限期

各項含義按 README 原文:

  • cwd:默認工作目錄,缺省爲 process.cwd()
  • timeoutMs:前臺命令默認超時。
  • maxTimeoutMs:單次超時上限。
  • maxOutputBytes:每條流的輸出上限,超限截斷並標記 lossy
  • graceMs:先發 SIGTERM,再等這麼久才 SIGKILL。

如果走 cordis.patch.yml,README 特別指出:patch 的 name 字段只做校驗、不能替換插件。正確做法是先把原 bash 條目設爲 disabled: true,再 insert 本插件。改完後重啓 dsh web 生效。bash 工具、後臺任務、hooks 橋的輸出都會經過編碼檢測。

倉庫另外寫了一種本地 link 的接入方式(DSH 要求 Node ^22.19 || >=24):

cd /path/to/dsh-bash-encoding && pnpm install && pnpm build
cd "${DSH_HOME:-$HOME/.dsh}/profiles/web"
pnpm add -w link:/path/to/dsh-bash-encoding

npm install 時還有一個版本號坑:peerDependencies.cordis 聲明爲 ^4.0.0-rc.7,而 DSH npm 發版把內置 cordis 按 0.0.1-rc.? 統一預發佈號發出去,可能報 ERESOLVE。README 的處理是加 --legacy-peer-deps。經 dsh plugin 或 pnpm 安裝會自動處理,運行不受影響。

適用場景與注意事項

適合誰:在 Windows 上跑 DSH、bash 走 WSL,並且經常看到中文亂碼的人。典型觸發條件同時滿足下面三條:

  • 操作系統是 Windows,DSH 在 Windows 側,bash 經 WSL 執行。
  • WSL 網絡仍是 NAT(%UserProfile%\.wslconfig 未設置 networkingMode=mirrored)。
  • 環境變量 HTTP_PROXY / HTTPS_PROXY 指向 localhost

附帶能修好的還有:GBK 中文工具、UTF-16 輸出,以及警告和命令輸出混在同一管道的情況。

不適合、或者明確做不到的,倉庫寫得很直接:

  1. Windows 原生(無 WSL)profile 默認停用。 平臺層已經插入 pwsh-sandboxSandboxPwshExecutor),同樣會註冊 ctx.bash;兩邊一起開會啓動失敗(service bash has been registered)。本插件用 bash -c spawn,面向 POSIX bash 棧,本來也不適用 pwsh 棧。這是 profile 配置層的停用,不是代碼棄用。
  2. read 工具讀 GBK / UTF-16 文件 不在當前範圍,README 標成路線圖 v2。
  3. node-pty 交互終端tool-pty / dsh-web-terminal)修不了:node-pty 內部已經按 UTF-8 有損解碼,插件拿不到原始字節。
  4. 不改 subprocess 核心層,避免替換基礎服務。
  5. 超過 maxOutputBytes 時保留頭部並標記 lossy,不做 spill 文件。
  6. 繼承環境裏 DSH_* 前綴、以及名稱含 KEY / PASSWORD / SECRET / TOKEN 的變量會被清掉,託管變量經 dshEnv 顯式傳入,這點和 DSH 官方 bash 縫合線的 hygiene 對齊。

如果只是想少看那條 WSL 代理警告,也可以從環境側處理:把 .wslconfignetworkingMode 改成 mirrored,或設置 WSL_UTF8=1。這是和插件互補的做法,不是替代關係。

沙箱方面,插件會在運行時探測 ctx.sandbox / ctx.sandboxPolicy,非 full-access 走 confine 路徑,sandboxMode 惰性讀取。它沒有客戶端 bundle,不受 snapshot0809 客戶端插件機制(dshClient 聲明 / ClientPackageCompositionError)影響。

小結

dsh-bash-encoding 做的事情很窄:把 bash 輸出的原始字節留下來,按 UTF-16LE / UTF-8 / GBK 等編碼解開,專門對付 Windows + WSL 下那類「每次命令都亂碼」的情況。它不是官方內置插件,星標也不高,但對卡在這條鏈路上的人,比在模型側猜測亂碼更直接。安裝前檢查源碼和 BSD-3-Clause 許可證,需要可復現環境時固定 commit。

目錄頁:https://deepseek-harness-plugin.com/zh-CN/plugins/dsh-bash-encoding/

GitHub:https://github.com/lhh010/dsh-bash-encoding

羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

小夜