从零给 DeepSeek Harness 写一个壁纸皮肤插件(已开源)

0 阅读5分钟

给 AI 编程工具换皮肤是什么体验?本文以我开发的 dsh-skin-alphacoders 为例,完整拆解 DeepSeek Harness(DSH)的插件机制、一个壁纸皮肤插件的开发全流程,以及开发中踩过的坑。文末附 GitHub 仓库,欢迎 Star。


一、项目是什么

DeepSeek Harness 是 DeepSeek 推出的编码 Agent 运行平台(dsh web 启动一个 Web GUI,默认 http://127.0.0.1:3080)。它本身支持浅色/深色/跟随系统三种主题,但没有自定义背景的能力。

dsh-skin-alphacoders 是一个皮肤插件:把 Alphacoders 热门壁纸 变成 Web GUI 的背景,功能包括:

  • 🖼️ 壁纸库:浏览热门壁纸(15 张/页、分页、懒加载)
  • 🔍 搜索:关键词搜索(走 Alphacoders 官方搜索页)
  • 📤 本地图片:上传自己的 PNG/JPEG/WebP/GIF 做壁纸,重启不丢
  • 🎨 显示控制:四种填充方式 + 0~90% 遮罩强度
  • 🌗 明暗联动:浅色白雾 / 深色黑雾遮罩自动切换
  • 💾 偏好持久化:写入 settings.yaml,重启自动恢复

壁纸效果

设置面板


二、DSH 插件机制:一个插件是怎么跑起来的

这部分是干货,理解了它,你也能给 DSH 写任何插件。

1. profile 与组合层

DSH 用 profile 组织运行配置,位于 $DSH_HOME/profiles/<name>web 是一个内置 profile,它的配置树由若干层按顺序叠加

  1. dsh.profile.bundles 里声明的组合包 patch(@deepseek-ai/dsh-base@deepseek-ai/dsh-web-app
  2. profile 自己的 cordis.patch.yml
  3. home 级 $DSH_HOME/cordis.patch.yml

每个 patch 就是一堆插件行id + name(包名)+ config。给 web 加第三方插件的官方方式是:

dsh plugin --profile web add <包名>

然后在 cordis.patch.yml 里登记一行:

- insert:
    - id: skin-alphacoders
      name: 'dsh-skin-alphacoders'

2. 双半插件包(Host + Client)

DSH 插件运行在 Cordis 运行时上。Web 场景下一个包有两个入口:

  • node 半exports["."]):跑在 Host 进程,能访问文件、网络、settings 服务、HTTP 服务器;
  • 浏览器半exports["./client"]):跑在页面里,负责 UI 与 DOM。

package.json 里用 dsh.client 声明浏览器半:

"dsh": {
  "client": {
    "platform": "web",
    "immediately": false,
    "inject": [
      "@deepseek-ai/dsh-client-runtime",
      "@deepseek-ai/dsh-client-locale",
      "@deepseek-ai/dsh-client-ui-slots"
    ]
  }
}

3. 客户端模块系统:__DSH_BOOT__

Web 壳启动时,host 侧会扫描浏览器插件名录,把配置项图注入首页的 window.__DSH_BOOT__。浏览器半的产物不是普通 ESM——它必须是一个单文件 + CJS 工厂

window.__ModuleLoader__.load({
  id: "dsh-skin-alphacoders",
  factory: (require) => {
    var module = { exports: {} }
    var exports = module.exports
    // ……你的 CJS 代码……
    return module.exports
  }
})

工厂里的 require 由模块系统提供(reactreact/jsx-runtime 在 seed 表里),所以外部依赖只能引用模块表里的包,其余依赖必须打进 bundle。这就是为什么我用 tsdown 单独构建 client 为 CJS、再写了个 wrap-client.mjs 脚本做包装。

4. 主题系统:Token + ThemeRuntime

DSH 的配色是一套两层 CSS 变量:--dsw-static-*(静态色板)+ --dsw-alias-*(语义别名,如 --dsw-alias-bg-base)。客户端有 ctx.theme(ThemeRuntime)服务:

  • register(theme):注册第三方主题
  • overrideTokens(source, tokens):在活动主题上叠加一层 token 覆盖(每个 token 要给 light/dark 两套值)
  • theme/change 事件:主题切换、系统配色变化时触发

壁纸皮肤的"表面透出壁纸"就是通过 overrideTokens 把背景系 token 改成 rgba(..., 0.55) 实现的——走官方机制,不直接改 DOM。

5. settings 的白名单,与绕行方案

DSH 的 settings 有 wire 白名单:浏览器经 api-proxy 只能读写 localeui-theme 等内置 namespace,仓库外插件注册的 namespace 一律返回 settings-not-exposed(这是我踩的第一个大坑)。

解决方案:Host 半仍用 ctx.settings.register('ui-skin', schema) 注册并持有 owner scope(进程内读写不受白名单限制),浏览器侧则走插件自己的 HTTP route 完成读写——数据最终还是落在 settings.yamlui-skin 分节。这是 DSH 为第三方插件预留的正规扩展点:ctx.webServer.register({ kind: 'prefix', path: '/skin-alphacoders', handler })


三、开发流程复盘

1. 需求先行

先写了需求文档:功能(壁纸库/应用/持久化/明暗联动)、非功能(性能/可读性/合规)、技术方案、验收标准。后续每一轮迭代都回填实现状态。

2. 调研:读文档 + 实测数据源

DSH 随包分发文档,各插件包都有 README(dsh-client-ui-themedsh-settingsdsh-host-webserver…),加上 cordis-plugin-development SKILL。Alphacoders 则是实测:发现热门页是服务端渲染的 schema.org ImageObject(15 条/页、?page=N 分页),图片 CDN 无鉴权直连,还挖出了 thumb-1920-<id> 中等尺寸变体——避免为背景下载 5K/8K 原图。

3. 架构:职责按平台切分

Host 半:抓取+解析 alphacoders(含搜索)+ TTL 缓存
        + settings 注册 + 本地图片存储 + HTTP route
Client 半:皮肤 store + 壁纸背景层 + 设置页 UI(React)

浏览器永远只跟同源的 /skin-alphacoders/* 打交道,没有 CORS 面。

4. 构建链

  • Host 半:tsdown 输出 ESM 单文件;
  • Client 半:tsdown 输出 CJS 单文件,wrap-client.mjs 再包成 __ModuleLoader__ 工厂;
  • schemastery 等浏览器模块表里没有的依赖用 deps.alwaysBundle 打进 bundle。

5. 隔离测试 + E2E

用一个独立的 DSH_HOME + 3090 端口起测试实例(不动正式 3080),HTTP 层用脚本验证,UI 层用 puppeteer-core 驱动系统 Chrome 跑 23 项断言:画廊渲染、应用壁纸、明暗联动、持久化、搜索、本地上传、文件清理、卸载恢复……

6. 踩坑记录

解法
settings 白名单 settings-not-exposedHost 持 owner scope + 插件自有 route 读写
overrideTokens 会同步发布 theme/change,形成重入风暴(实测 500+ 层递归)token 覆盖层按签名去重,输入不变绝不重建
pnpm file: 依赖是硬链接副本,改源码不生效开发期用目录联接(Junction)直连源码
全表面半透明导致文字不清(用户反馈)迭代出最终方案:主表面透壁纸,但设置面板的 --dsw-alias-bg-layer-2 不覆盖,面板保持不透明、文字清晰

四、安装使用

# 1. 构建
npm install && npm run build

# 2. 安装进 web profile
dsh plugin --profile web add "file:<本包绝对路径>"

# 3. $DSH_HOME/profiles/web/cordis.patch.yml 登记插件行
# - insert:
#     - id: skin-alphacoders
#       name: 'dsh-skin-alphacoders'

重启 dsh web,打开「设置 → 壁纸」即可使用。


五、欢迎体验与共建

如果你也在折腾 DeepSeek Harness,欢迎 Star、提 Issue 或 PR;后续计划:官方 API 通道、壁纸自动轮换、配套主题色。

壁纸版权归原作者与 Alphacoders 所有,本插件仅供个人使用。