Skip to content
dsh.fish
Bundle

dsh-desktop-settings

为 DSH 桌面版补充官方设置中缺失的几节:README 翻译(DeepL)配置与连通性自测、GitHub 加速代理(自建反向代理,默认关闭)配置与连通性自测、插件市场缓存清理,以及 DeepSeek 开放平台的充值入口。

Source
VCPr0j3k7
License
MIT
Updated
Updated 24 days ago

Readme

# dsh-desktop-settings

[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)
[![Node.js](https://img.shields.io/badge/node-%5E22.19.0%20%7C%7C%20%3E%3D24-339933.svg)](./package.json)

[English](./README.en.md) | **简体中文**

为 DeepSeek Harness (DSH) 桌面版补充官方设置中缺失的几节:README 翻译(DeepL)配置与
连通性自测、GitHub 加速代理(自建反向代理)配置与连通性自测、插件市场缓存清理,以及
DeepSeek 开放平台的充值入口。

本插件以独立 bundle 形式装入 DSH profile,不修改任何官方代码。

---

## 目录

- [问题背景](#问题背景)
- [环境要求](#环境要求)
- [安装](#安装)
- [卸载](#卸载)
- [工作原理](#工作原理)
- [功能与设置项](#功能与设置项)
- [配置文件](#配置文件)
- [HTTP 路由](#http-路由)
- [刻意不实现的能力](#刻意不实现的能力)
- [启用条件](#启用条件)
- [测试](#测试)
- [已知限制](#已知限制)
- [许可](#许可)

## 问题背景

官方设置页已覆盖 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:

```bash
# 用官方桌面版随包的 pnpm 装进 profile
pnpm add "file:<插件目录绝对路径>"
# 或者直接从 GitHub 装
pnpm add "github:VCPr0j3k7/dsh-desktop-settings"
```

示例(路径按实际安装位置替换):

```bash
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` 数组**,否则插件
不会加载 —— 这是本项目的关键坑:

```jsonc
{
  "dsh": {
    "profile": {
      "bundles": [
        // …已有条目…
        "dsh-desktop-settings"
      ]
    }
  }
}
```

`pnpm add` 只会把包写进 `dependencies` 并安装到 `node_modules`。声明了
`dsh.bundle.patch` 的插件必须在 `dsh.profile.bundles` 中登记才会被挂载;仅安装而不登记时,
插件加载过程没有任何报错,界面上也看不出差异,只是它的设置分区与侧边栏入口都不会出现。
官方 CLI 的 `dsh plugin add` 会自动完成这一步,手动执行 pnpm 时需自行补齐。

安装完成后必须**重启 DeepSeek Harness**。宿主只在启动时装配插件树,运行中的实例不会
热更新。

验证是否生效:

1. 重启后,设置页应出现「桌面设置补充」分区,侧边栏应出现「API 充值」入口;
2. 打开开发者工具,控制台应出现:

   ```text
   [dsh-desktop-settings] client factory 已执行
   [dsh-desktop-settings] 宿主基址 = (相对路径)
   ```

   第一行说明客户端半边已被加载,第二行说明它已连上宿主。只有第一行而没有第二行,
   表示客户端未能连上宿主。

## 卸载

```bash
cd "$PROFILE"
"$NODE" "$PNPM" remove dsh-desktop-settings
```

随后把 `dsh.profile.bundles` 中的 `dsh-desktop-settings` 条目一并删除,并重启
DeepSeek Harness。

若要临时停用而不卸载,只需从 `dsh.profile.bundles` 中移除该条目后重启。

## 工作原理

本插件由「两个半边」组成:

```text
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 代理与设置辅助能力
```

### 宿主半边

1. 在 `ctx.webServer` 上注册**一条** `kind: 'prefix'` 路由,前缀为
   `/dsh-desktop-settings/api`,前缀内部的派发由 `host/router.mjs` 完成。注册点只有一处,
   卸载时由框架统一清理。
2. 所有响应统一为 `{ ok: true, data }` 或 `{ ok: false, error }`,业务失败也返回 HTTP 200;
   状态码只用于表达「路由不存在」这类传输层事实。
3. 进度事件进入环形队列(上限 400 条),客户端以 `GET /events?since=<seq>` 轮询增量。
   不采用 SSE,是因为外壳对自定义 scheme 的转发是否会缓冲响应体无法确认,轮询没有这个
   未知数。

### 客户端半边

1. 页面由官方外壳提供,其来源(origin)有三种可能:宿主自身的 HTTP 地址、外壳的自定义
   scheme(`dsh-app://app/`)、不透明来源(`location.origin === "null"`)。因此传输层在
   第一次请求时按顺序尝试候选基址(相对路径 → `http://dsh.internal`),并以 `/info` 的
   返回内容判定哪条真正可用。
2. 判定条件是 `data.plugin` 等于本插件 id,而不是「返回 200」—— SPA 的兜底路由会把未知
   路径也回成 200 加一段 HTML。
3. 选定基址后记录,后续请求直接复用;全部候选都不可用时回落到相对路径,并只在控制台留下
   一行提示,不反复重试。

## 功能与设置项

### 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` | `/api` |
  | `raw.githubusercontent.com` | `/raw` |
  | `codeload.github.com` | `/codeload` |
  | `github.com` | `/gh` |
  | `objects.githubusercontent.com` | `/objects` |
  | `release-assets.githubusercontent.com` | `/release-assets` |

- 302 由本插件手动跟随(上限 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)。若插件突然失效,应首先检查该条目是否仍然存在。

## 测试

```bash
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。

## 许可

[MIT](./LICENSE)

Install

dsh plugin --profile web add github:VCPr0j3k7/dsh-desktop-settings

Profile: web

  • This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.
Source