dsh-desktop-settings
English | 简体中文
为 DeepSeek Harness (DSH) 桌面版补充官方设置中缺失的几节:README 翻译(DeepL)配置与 连通性自测、GitHub 加速代理(自建反向代理)配置与连通性自测、插件市场缓存清理,以及 DeepSeek 开放平台的充值入口。
本插件以独立 bundle 形式装入 DSH profile,不修改任何官方代码。
目录
问题背景
官方设置页已覆盖 account / general / models / plugins / subagent / web-search / agent-loop / shell 等节,但以下几项没有对应入口:
| 缺失项 | 影响 |
|---|---|
| README 翻译 | 插件市场中大量仓库只有英文 README,中文用户不易判断用途 |
| GitHub 加速代理 | 直连 GitHub 常因 TLS 握手与首字节延迟而缓慢或失败,缺少统一的出口配置 |
| 缓存清理 | 市场列表缓存、下载残留与翻译缓存没有清理入口 |
| 充值入口 | 余额不足时需要自行查找充值页 |
本插件只补齐这四项。以下三节刻意不提供:「应用名称」「DSH 数据目录(DSH_HOME)」
「从其他 DSH 目录沿用配置」—— 它们的前提是外壳会划出一个完全隔离的数据目录,而官方
桌面版不隔离 ~/.dsh,只存在一个家目录,因此这三节没有存在的前提。
环境要求
| 项 | 要求 |
|---|---|
| Node.js | ^22.19.0 || >=24 |
| DeepSeek Harness | 官方桌面版(宿主半边依赖其 process.argv 布局与 profile 结构) |
宿主半边只依赖 ctx.webServer;客户端半边依赖官方客户端模块加载器与
@deepseek-ai/dsh-client-ui-slots 提供的槽位。
安装
用官方桌面版随包的 pnpm 装进目标 profile:
# 用官方桌面版随包的 pnpm 装进 profile
pnpm add "file:<插件目录绝对路径>"
# 或者直接从 GitHub 装
pnpm add "github:VCPr0j3k7/dsh-desktop-settings"
示例(路径按实际安装位置替换):
PNPM="D:/DeepSeek Harness/resources/runtime/pnpm/bin/pnpm.cjs"
NODE="D:/DeepSeek Harness/resources/runtime/primary-runtime/dependencies/node/bin/node.exe"
PROFILE="C:/Users/<用户名>/.dsh/profiles/desktop"
cd "$PROFILE"
"$NODE" "$PNPM" add "file:C:/path/to/dsh-desktop-settings"
装完还必须把包名加进 profile package.json 的 dsh.profile.bundles 数组,否则插件
不会加载 —— 这是本项目的关键坑:
{
"dsh": {
"profile": {
"bundles": [
// …已有条目…
"dsh-desktop-settings"
]
}
}
}
pnpm add 只会把包写进 dependencies 并安装到 node_modules。声明了
dsh.bundle.patch 的插件必须在 dsh.profile.bundles 中登记才会被挂载;仅安装而不登记时,
插件加载过程没有任何报错,界面上也看不出差异,只是它的设置分区与侧边栏入口都不会出现。
官方 CLI 的 dsh plugin add 会自动完成这一步,手动执行 pnpm 时需自行补齐。
安装完成后必须重启 DeepSeek Harness。宿主只在启动时装配插件树,运行中的实例不会 热更新。
验证是否生效:
重启后,设置页应出现「桌面设置补充」分区,侧边栏应出现「API 充值」入口;
打开开发者工具,控制台应出现:
[dsh-desktop-settings] client factory 已执行 [dsh-desktop-settings] 宿主基址 = (相对路径)第一行说明客户端半边已被加载,第二行说明它已连上宿主。只有第一行而没有第二行, 表示客户端未能连上宿主。
卸载
cd "$PROFILE"
"$NODE" "$PNPM" remove dsh-desktop-settings
随后把 dsh.profile.bundles 中的 dsh-desktop-settings 条目一并删除,并重启
DeepSeek Harness。
若要临时停用而不卸载,只需从 dsh.profile.bundles 中移除该条目后重启。
工作原理
本插件由「两个半边」组成:
dsh-desktop-settings/
├── package.json 声明 dsh.bundle.patch 与 dsh.client.platform
├── cordis.patch.yml 宿主侧挂载声明
├── index.js 宿主半边:Cordis 插件,在 /dsh-desktop-settings/api 下注册路由
├── client.js 客户端半边:window.__ModuleLoader__.load({ id, factory })
└── host/ 适配层与服务实现
├── env.mjs 把 process.argv 解析成路径与宿主上下文
├── router.mjs HTTP 门面(前缀派发、{ok,data|error}、事件队列)
├── config.mjs 共享配置(<DSH_HOME>/dsh-extras.json)
├── system.mjs 打开浏览器 / 选择目录 / 打开文件夹
├── logger.mjs 输出捕获与日志环形缓冲
├── electron-shim.mjs app.getPath('userData') 的替身
├── yaml-libs.mjs 借用官方所使用的两个 YAML 库(多候选路径)
└── services/ 翻译、GitHub 代理与设置辅助能力
宿主半边
- 在
ctx.webServer上注册一条kind: 'prefix'路由,前缀为/dsh-desktop-settings/api,前缀内部的派发由host/router.mjs完成。注册点只有一处, 卸载时由框架统一清理。 - 所有响应统一为
{ ok: true, data }或{ ok: false, error },业务失败也返回 HTTP 200; 状态码只用于表达「路由不存在」这类传输层事实。 - 进度事件进入环形队列(上限 400 条),客户端以
GET /events?since=<seq>轮询增量。 不采用 SSE,是因为外壳对自定义 scheme 的转发是否会缓冲响应体无法确认,轮询没有这个 未知数。
客户端半边
- 页面由官方外壳提供,其来源(origin)有三种可能:宿主自身的 HTTP 地址、外壳的自定义
scheme(
dsh-app://app/)、不透明来源(location.origin === "null")。因此传输层在 第一次请求时按顺序尝试候选基址(相对路径 →http://dsh.internal),并以/info的 返回内容判定哪条真正可用。 - 判定条件是
data.plugin等于本插件 id,而不是「返回 200」—— SPA 的兜底路由会把未知 路径也回成 200 加一段 HTML。 - 选定基址后记录,后续请求直接复用;全部候选都不可用时回落到相对路径,并只在控制台留下 一行提示,不反复重试。
功能与设置项
README 翻译(DeepL)
- 开关默认开启。翻译只对「基本不含中文」的文本调用:CJK 字符少于 30 个且占比低于 8% 时 才发送请求,因此真正的中文 README 不会消耗配额。
- 关键词提取完全在本地完成,不受翻译开关影响,离线可用。
- 翻译结果按仓库缓存在磁盘上,重复打开不会重复计费。
- 连通性自测:
POST /settings/test-translation会翻译一句固定文本,返回检测到的源语言、 目标语言与译文。若返回 456 表示配额用尽,403 表示密钥无效或 Free / Pro 端点选错。 - DeepL Free 版密钥以
:fx结尾,必须使用api-free.deepl.com主机;认证头是Authorization: DeepL-Auth-Key <key>,不是 Bearer。
GitHub 加速代理
默认关闭且地址留空。代理地址属于搭建者本人,插件不预置任何值,也不会把使用者的请求 静默导到某台机器上。关闭时行为与没有该功能时完全一致。
开启条件是开关打开且地址非空;只开开关不填地址时静默回落到官方地址,而不是报错。
地址会被归一化:容忍未写协议、结尾多一个斜杠、反向代理挂在子路径下三种情况;协议只认 http / https,其它一律判为无效并回落直连。
路径前缀与上游域名一一对应,令牌、Accept 头与请求方法均原样透传:
上游域名 代理路径前缀 api.github.com/apiraw.githubusercontent.com/rawcodeload.github.com/codeloadgithub.com/ghobjects.githubusercontent.com/objectsrelease-assets.githubusercontent.com/release-assets302 由本插件手动跟随(上限 6 跳),拿到
Location后把其中的 GitHub 域名重新改写为 代理地址再请求。若交给 fetch 自动跟随,它会直接连接重定向目标域名,等于绕开代理。连通性自测:
POST /settings/test-github-proxy打两个探针 —— 代理根路径与一条真实的 raw 请求(electron/electron的 README)。两者都必须通过:根路径正常只说明反向代理 在运行,不代表它能连上 GitHub。可选令牌
githubToken默认留空。留空时按匿名请求发送(每小时 60 次);填入个人访问 令牌后走认证额度(每小时 5000 次)。令牌仅用于访问 GitHub API,不写入日志,也不随 插件分发。
清理缓存
POST /market/clear 删除以下内容:
| 目标 | 说明 |
|---|---|
<DSH_HOME>/plugin-data/market-cache.json |
插件市场列表缓存 |
<DSH_HOME>/plugin-data/market-downloads |
下载残留目录 |
<DSH_HOME>/plugin-data/translate-cache/ |
翻译缓存 |
单个目标删除失败时会被跳过,不会让整个操作失败。
API 充值
GET /recharge/status返回充值页地址、用量页地址、API Key 的来源与当前数据目录。- API Key 来源只做存在性探测,不读取也不回传密钥本体:优先看环境变量
DEEPSEEK_API_KEY,其次检查<DSH_HOME>/.credentials.yaml中是否存在该键。 POST /recharge/open用系统默认浏览器打开https://platform.deepseek.com/top_up。 打开地址前会校验协议,只放行 http / https。- 侧边栏的「API 充值」图标点击后直接打开浏览器,同时主面板切到充值页。
配置文件
所有设置统一写入 <DSH_HOME>/dsh-extras.json,与插件市场(dsh-plugin-market)共用
同一份文件:写入方是本插件,读取方还包括需要翻译与下载的市场模块。采用文件而不是插件间
HTTP 调用,是因为文件不存在「另一个插件没装 / 还没启动」的时序问题。
| 字段 | 默认值 | 说明 |
|---|---|---|
appName |
"DeepSeek Harness" |
显示名 |
githubProxy.enabled |
false |
是否启用 GitHub 加速代理 |
githubProxy.baseUrl |
"" |
代理地址,形如 http://1.2.3.4:8080 |
githubToken |
"" |
可选的 GitHub API 令牌 |
translate.enabled |
true |
是否启用 README 翻译 |
translate.endpoint |
https://api-free.deepl.com/v2/translate |
DeepL 端点 |
translate.apiKey |
"" |
DeepL API Key |
translate.targetLang |
"ZH" |
目标语言代码(DeepL 使用大写) |
translate.characterCount |
0 |
本地累计的已翻译字符数 |
translate.maxCharacters |
4000 |
单个 README 最多翻译的字符数 |
配置读取不做缓存:文件只有几百字节,每次重读的成本可以忽略,换来的是「改完立刻生效」。 未知字段会原样保留,缺失字段回落到默认值。
首次运行时,若 <DSH_HOME>/dsh-extras.json 尚不存在,会尝试从
%APPDATA%/DSH-Desktop/config.json(或 DSH Desktop、dsh-desktop)迁移一次
translate 与 githubProxy,使此前填写过的密钥与代理地址可以继续使用。
HTTP 路由
宿主半边在 /dsh-desktop-settings/api 前缀下提供以下路由。
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /info |
版本、数据目录、运行时目录、宿主状态与本插件 id |
| GET | /config |
读取完整配置(不含内部路径字段) |
| POST | /config |
深合并写入配置,返回写后的完整配置 |
| POST | /open-external |
用系统默认程序打开网址(仅 http / https) |
| POST | /pick-directory |
选择目录:优先官方 directoryPicker,退回 PowerShell 文件夹对话框 |
| GET | /restart-pending |
profile 关键文件是否晚于宿主就绪时刻被修改 |
| POST | /restart-host |
刻意拒绝,见下文 |
| GET | /events |
增量拉取事件队列,参数 since |
| POST | /settings/test-translation |
DeepL 连通性自测 |
| POST | /settings/test-github-proxy |
GitHub 加速代理连通性自测 |
| POST | /market/clear |
清理市场缓存、下载残留与翻译缓存 |
| GET | /recharge/status |
充值页信息与 API Key 来源 |
| POST | /recharge/open |
打开充值页 |
| GET | /settings/inspect-homes |
刻意报「不适用」,见下文 |
| POST | /settings/import-from |
刻意报「不适用」,见下文 |
请求体上限为 8MB;未知路由返回 404 与 JSON 响应体。
刻意不实现的能力
以下三条路由存在,但会明确回报失败,而不是静默忽略或返回空结果。
跨 DSH_HOME 的配置探测与导入
GET /settings/inspect-homes 与 POST /settings/import-from 对应「从另一个 DSH 数据目录
沿用配置」这一能力。它的前提是外壳划出完全隔离的数据目录,而官方桌面版不隔离 ~/.dsh,
只存在一个家目录,因此该能力不适用。两条路由会返回可读的原因说明,而不是 404 —— 这样在
有人手工构造请求时能直接得到解释。
host/services/settings-ops.mjs 中保留了该能力的完整实现,供需要隔离语义的环境使用。
重启宿主
POST /restart-host 会返回
插件无法重启宿主进程,请手动退出并重新打开 DeepSeek Harness。
在某些外壳中 DSH 是其 spawn 出来的子进程,因此可以重启;但插件本身就是宿主加载的一部分, 无法让宿主重启自己,而尝试杀死进程会一并丢失用户的会话。因此这里如实拒绝,由界面把 「请手动重启」显示给用户。
启用条件
| 半边 | 启用条件 |
|---|---|
| 宿主半边 | 包名出现在 profile package.json 的 dsh.profile.bundles 数组中;宿主在启动时装配插件树 |
| 客户端半边 | dsh.client.platform === 'web',且运行环境提供官方客户端模块加载器与 @deepseek-ai/dsh-client-ui-slots 槽位 |
客户端半边向以下槽位注册内容:
| 槽位 | 内容 |
|---|---|
settings.section |
「桌面设置补充」分区(order: 30) |
sidebar.panellist |
「API 充值」侧边栏入口(order: 44),点击即打开充值页 |
main |
充值页主面板 |
官方桌面版的插件库开关在保存时会重写 dsh.profile.bundles(内部调用
sanitizeProfile(...) 清理非官方 bundle)。若插件突然失效,应首先检查该条目是否仍然存在。
测试
npm test
# 等价于
node test/check.mjs
test/check.mjs 为离线自检,只使用 Node 内置模块,不依赖 DSH 运行时,覆盖:
- 宿主半边可被 import,且导出
name/inject/apply; package.json中dsh.bundle.patch指向的文件真实存在;dsh.client.platform === 'web',且exports['./client']指向的文件真实存在;- 路由对齐:从
client.js中提取全部被调用的路由路径,逐条确认在index.js的路由表 中都有实现; host/config.mjs的默认值包含githubToken字段。
端到端验证需要在真机上进行:安装并重启桌面版后,设置页应出现「桌面设置补充」分区, 侧边栏应出现「API 充值」入口,两个「测试连通性」按钮应能返回结果。
已知限制
- 安装后必须手动登记
dsh.profile.bundles。 手动执行 pnpm 安装时不会自动登记,漏掉 这一步的表现为插件完全静默 —— 无报错、无日志、界面上没有任何变化。 - 客户端半边能否连上宿主只能在真窗口里确认。 页面来源存在三种可能,因此传输层采用 按顺序探测的方式;选错时会在控制台留下「宿主基址 = …」一行。
- 本仓库不包含反向代理的服务端配置。 代码注释中引用的
deploy/ghproxy.conf是 搭建反向代理时的 nginx 配置示例,未随本包分发。路径前缀与 location 必须与上表一致, 对不上不会报错,只会安静地返回 404。 - 自建反向代理多为明文 HTTP。 启用
githubToken时 Authorization 头会经它明文发出, 开启前请确认链路可信;建议不要在启用代理的同时配置令牌。 - 插件无法重启宿主。 安装或修改配置后需要手动退出并重新打开 DeepSeek Harness。
- 翻译配额由本地计数近似统计。
translate.characterCount只累加本插件发出的字符数, 与 DeepL 账户的实际用量可能不一致,仅用于在设置页显示大致规模。 - 官方若变更 profile 结构或
process.argv布局,宿主半边的路径解析会失效。 相关解析 集中在host/env.mjs,失效时表现为无法定位 profile 或随包 pnpm。
No comments yet. Be the first to write one.