DeepSeek Harness Plugin Hub

Publish and manage complete Harness Profiles. Discover Plugins for your next setup.

Explore

PluginsPresetsDocsNews

Community

Publish a pluginContactReport an issue

Resources

Plugin Hub on GitHubDeepSeek HarnessSystem statusPrivacy notice
© 2026 DeepSeek Harness Plugin HubPowered byPaxTech

Independent and unofficial. Not affiliated with, authorized by, or endorsed by DeepSeek.

Deepworks — DSH Plugin for DeepSeek Harness
← Plugins

dsh-deepworks

Deepworks

DeepSeek Harness bundle: the DeepWorks adaptation layer (branding today; model config and sign-in later)

The plugin will be installed here. Keep web if you are unsure.

npx -y @deepseek-ai/dsh plugin --profile web add dsh-deepworks@0.1.0
READMECompatibilityVersions

Compatibility and provenance

Deepworks is published as dsh-deepworks and currently resolves to version 0.1.0. The Hub verifies its manifest and preserves the exact installation source for reproducible installs.

DSH compatibility
*
Runtime surfaces
any
Release source
npm
Registry updated
9/23/2026

Versions

0.1.0stable
8/17/2026
Latest
0.1.0
DSH
*
HMR
Process restart
Tree shaking
Safe tree shaking not declared
Unpacked size
108.9 kB
Files
10
Surface
any
License
MIT
Source
npm
GitHub
★ 0
Weekly downloads
6
View source ↗
Project homepage ↗
README badge

Click the badge to copy Markdown for your README.

Do you maintain this Plugin?Claim benefit · Priority security scan

Verify the GitHub repository declared in package.json to manage this listing. After you claim it, Hub will prioritize a security scan of the current version and publish the result when it passes.

Claim this Plugin →
Report an issue
DeepSeek Harness Plugin Hub
ProfilesPluginsCategoriesNewsDocsSign inManage Profiles
ProfilesPluginsCategoriesNewsDocsSign in

README

dsh-deepworks

DeepWorks 对 DeepSeek Harness 的适配层。这是一个可安装的组合包(bundle):装上就生效,不需要 --patch,也不需要改 harness 源码。

dsh plugin --profile web add dsh-deepworks
dsh web

现在这一层只有品牌:Web UI 里露出的 DeepSeek Harness 标识换成 DeepWorks 的。模型配置与登录认证是计划中的下两层,骨架已经留好,见后续两层。

现在换掉了什么

位置换成
侧边栏左上角的横幅(展开态)——鲸鱼 + deepseek 字标 + HARNESS 徽章assets/deepworks-wordmark.png(横向字标)
侧边栏收起态、空态大图里的鱼形标记assets/deepworks-logo.png(方形标记)
浏览器标签页图标同上那张方形标记
浏览器标签页标题 DeepSeek HarnessDeepWorks

两张图取自 fastagi-web-obs.deepexi.com 的 deepworks-wordmark@2x.png 与 deepworks-logo.png,已落盘进包内。

安装

三种交付方式,效果一样,代价不同:

方式命令说明
npmdsh plugin --profile web add dsh-deepworks发布时已构建好 lib/,安装零摩擦
tarballdsh plugin --profile web add ./dsh-deepworks-0.1.0.tgzpnpm pack 产出,同样是预构建产物
GitHubdsh plugin --profile web add github:Dbraum/dsh-deepworks#<sha>拉的是源码,靠 prepare 脚本现场构建,见下

从 git 安装时 pnpm 拉到的是源码不是构建产物,本包的 prepare 脚本会在安装后跑 tsdown 补上 lib/。但 pnpm 10+ 在得到显式允许前拒绝执行 git 依赖的 prepare,所以首次 add 会失败;把 pnpm 打印的包键写进该 profile 的 pnpm-workspace.yaml 再重试:

allowBuilds:
  dsh-deepworks: true

请如实看待这项授权:它允许本包的代码在安装时于你的机器上执行,且不在 agent 的任何沙箱之内。锁定 commit(#<sha>)能让后续推送无法悄悄改变实际运行的内容。不想让使用者做这项授权,就用 npm 或 tarball 分发。

卸载:dsh plugin --profile web remove dsh-deepworks,依赖和对应的层一起消失。

使用

两种用法,取决于你有没有 dsh:当组合包用是本包的本职,装上即生效、不写代码;当普通 npm 包用是给自建 cordis 应用的,需要自己挂载。

一、当 dsh 组合包用(主路径)

从零到看见效果,三条命令:

dsh plugin --profile web add dsh-deepworks   # 装包,patch 层自动并入 profile
dsh web                                       # 起 Web UI
# 浏览器打开输出的地址,侧边栏 logo、标签页图标和标题都已是 DeepWorks

生效不需要额外配置——图和标题都内置在包里。装上之后 $DSH_HOME/profiles/web/ 下会多出这一层,位置在 harness 自带两层之后、你自己那层之前(见原理的第二张图)。

想确认到底装没装上:dsh plugin 是把参数透传给 pnpm 的(dsh plugin --profile <name> <pnpm args>),所以 pnpm 的命令都能用:

dsh plugin --profile web list     # 透传成 pnpm list,应能看到 dsh-deepworks

要换成自己的图和标题,不改本包,在自己 profile 的 cordis.patch.yml 里按 id 覆盖,详见换成自己的图和标题。改完不用重启,刷新浏览器即可。

二、当普通 npm 包用(编程式)

如果你不是在 dsh 里,而是自己搭的 cordis 应用,可以直接装:

npm install dsh-deepworks          # 或 pnpm add / yarn add

前提两条:Node ^22.19.0 || >=24,以及本包是 纯 ESM("type": "module"),CommonJS 里只能用动态 import()。宿主还必须提供 webServer 服务——品牌层的 inject = ['webServer'] 声明了这个依赖,没有它插件不会启动。

包有两个 subpath 导出,各管一摊:

导出内容
dsh-deepworks品牌事实常量:PRODUCT_NAME、ASSETS(两张内置图的绝对路径)
dsh-deepworks/branding品牌层插件本体:apply、inject、name、Config,外加几个纯函数

挂载插件:

import { Context } from '@deepseek-ai/cordis'
import WebServer from '@deepseek-ai/dsh-host-webserver'
import * as branding from 'dsh-deepworks/branding'

const ctx = new Context()
await ctx.plugin(WebServer, { host: '127.0.0.1', port: 5140 })  // 必须先有 webServer
await ctx.plugin(branding, {})                                   // 用内置的 DeepWorks 图和标题

await 一个 fiber 表示等它加载完成。品牌层的 inject 会让它自己等 webServer 就绪,所以两句的先后不影响正确性,但 await 能让配置校验的报错当场抛出来,而不是变成一个没人接的 rejection。

传配置就是第二个参数,字段与默认值见配置项:

await ctx.plugin(branding, {
  images: {
    brand: '/absolute/path/to/my-wordmark.png',
    mark: '/absolute/path/to/my-logo.png',
  },
  title: '我的产品',
  targets: ['brand', 'mark'],
})

注意 images 只吃本地绝对路径或远程地址(https:// / // / data:),相对路径不行。配置在加载期校验:文件不存在、routePath 形状不合法、地址里有会越出 CSS 引号的字符,都会当场让插件启动失败并指名字段,不会拖到浏览器请求时才 404。

只用内置的品牌常量,不挂插件也行——比如你想在自己的页面里引用同一张图:

import { PRODUCT_NAME, ASSETS } from 'dsh-deepworks'

console.log(PRODUCT_NAME)      // 'DeepWorks'
console.log(ASSETS.wordmark)   // …/node_modules/dsh-deepworks/assets/deepworks-wordmark.png
console.log(ASSETS.logo)       // …/node_modules/dsh-deepworks/assets/deepworks-logo.png

ASSETS 给的是文件系统绝对路径(靠 import.meta.url 算出来的),不是 URL——可以直接 readFile,但要在浏览器里显示得你自己挂路由或复制到静态目录。

卸载:插件的每处注册都走 ctx.effect(),所以停掉 fiber 就全部回卷,路由变 404、index.html 的注入撤干净:

const fiber = await ctx.plugin(branding, {})
await fiber.dispose()

三、离线 / 内网安装

拿不到 npm registry 时用 tarball,产物完全一样:

pnpm pack                                                    # 本仓库里产出 dsh-deepworks-0.1.0.tgz
dsh plugin --profile web add ./dsh-deepworks-0.1.0.tgz       # 或 npm install ./dsh-deepworks-0.1.0.tgz

tarball 里是预构建产物,装的时候不跑构建、不需要联网,也不需要 allowBuilds 授权——这点和 git 安装相反(git 装的是源码,见安装)。

包的形状

一个包、一个 patch 层、一个版本;每一层是一个 subpath 导出,patch 里按 dsh-deepworks/<层> 引用。loader 拿 name: 当普通模块 specifier 去 import,所以子路径和包名一样能解析。

dsh-deepworks/
  cordis.patch.yml       # DeepWorks 适配层:现在一行 branding,以后加行
  assets/                # 品牌资产
  src/
    index.ts             # 各层共用的品牌事实(产品名、两张图的路径)→ 包的 "." 导出
    branding/index.ts    # 品牌层插件 → "./branding"

加一层就是:src/<层>/index.ts 写插件、package.json 的 exports 加一条、tsdown.config.ts 的 entry 加一条、cordis.patch.yml 加一行。src/index.ts 必须保持独立入口而不是被打进各层——品牌资产的路径靠 import.meta.url 算,它得稳定落在 lib/ 下一层深。

换成自己的图和标题

不要改本包,在自己 profile 的 cordis.patch.yml 里按 id 覆盖即可($DSH_HOME/profiles/<name>/cordis.patch.yml):

- id: deepworks-branding
  config:
    images:
      brand: '/absolute/path/to/my-wordmark.png'
      mark: '/absolute/path/to/my-logo.png'
    title: '我的产品'
    targets: ['brand', 'mark']

images 的值也可以直接写 https:// / // / data: 地址,那样插件不挂路由,浏览器自己去源站取图——图放在 OBS/CDN 上时省掉一次落盘:

- id: deepworks-branding
  config:
    images:
      brand: 'https://fastagi-web-obs.deepexi.com/deepworks/assets/deepworks-wordmark%402x.png'
      mark: 'https://fastagi-web-obs.deepexi.com/deepworks/assets/deepworks-logo.png'
    targets: ['brand', 'mark']

代价是浏览器要能连到那个域名(离线、内网隔离、CSP 收紧时就取不到,页面会退回空白框),而本地文件由 dsh 自己吐字节,没有这层依赖。

patch 是整块替换 config,不是按键深合并,所以要把想保留的字段一并重述——images 这一层同样是整块替换,只写 brand 时 mark 会退回包内自带的图。改完不用重启:patch 层被监听,进程会重新组合,刷新浏览器即可。换图片文件本身更简单——插件每次请求重新读盘,直接覆盖文件再刷新就行。

配置项

字段默认说明
images.brand包内 assets/deepworks-wordmark.png侧边栏左上角那条横幅用的图
images.mark包内 assets/deepworks-logo.png方形标记用的图,默认也是标签页图标
routePath/deepworks/logo本地图片挂载的路由前缀,每张落在 <前缀>/<目标>;需与站内已有路由错开
replaceFavicontrue是否连浏览器标签页图标一起换
faviconSource'mark'标签页图标取哪个目标的图
title'DeepWorks'浏览器标签页标题;给空串表示不动上游的 DeepSeek Harness
targets['brand', 'mark']换哪几处品牌图形,见下表

images 的值是本地绝对路径或远程地址二选一。本地路径指向不存在的文件、routePath 形状不合法、地址里含引号括号一类会越出 CSS 引号的字符,都在加载期直接让插件启动失败并指名是哪个字段——不留到浏览器请求时才 404。标题走的是另一条路:它是正常会出现 & 的用户文本,所以转义而不是拒绝。

支持 png / jpg / gif / webp / svg / ico,按扩展名给 Content-Type。图片以 background-size: contain 贴进原 svg 的框里,等比缩放不裁切,所以任何比例的图都不会变形。

targets 的两个取值对应两处互不重叠的内置组件:

取值换掉的东西出现位置适合放
brand品牌整块:鲸鱼 + deepseek 字标 + HARNESS 徽章画在同一个 svg 里侧边栏左上角(展开态)横向字标
mark单独的鱼形标记侧边栏收起态、空态大图方形标记

侧边栏展开时画的是 brand,它自带鲸鱼,所以只给 mark 不会影响侧边栏;要全站换干净就两个都写。faviconSource 那张图不受 targets 约束:写 targets: ['brand'] 只换左上角,标签页图标照样能用方形标记,插件会单独为它挂上路由。

原理

品牌层只有 Node 半,不声明 dsh.client,所以不需要浏览器打包。它只用了 ctx.webServer 的两个扩展点,每处注册都走 ctx.effect(),插件卸载时自动撤销:

flowchart LR
  PKG["package.json<br/>dsh.bundle.patch"] --> PATCH["cordis.patch.yml<br/>insert dsh-deepworks/branding"]
  PATCH --> APPLY["apply(ctx, config)<br/>inject: ['webServer']"]
  APPLY -->|"ctx.effect + register()"| ROUTE["每张本地图一条 HTTP 路由<br/>/deepworks/logo/brand、…/mark<br/>每次请求重新读盘"]
  APPLY -->|"ctx.effect + tapIndex()"| TAP["index.html 改写<br/>每个目标一组规则、换 favicon、换 title"]
  TAP --> HTML["浏览器拿到的 index.html"]
  ROUTE --> IMG["浏览器加载图片"]
  REMOTE["https:// 的图<br/>不挂路由"] --> IMG
  HTML -->|"CSS 选择器 svg[viewBox=…]"| SWAP["内置品牌图形被背景图顶替"]
  IMG --> SWAP

装上以后,本包这一层落在 profile 组合里 harness 自带的两层之后、使用者自己那层之前:

flowchart TB
  ROOT["空的 profile 根"] --> B1["@deepseek-ai/dsh-base"]
  B1 --> B2["@deepseek-ai/dsh-web-app"]
  B2 --> B3["dsh-deepworks<br/>本包的 cordis.patch.yml"]
  B3 --> P["profile 的 cordis.patch.yml<br/>← 使用者在这里覆盖 images / title"]
  P --> H["$DSH_HOME/cordis.patch.yml"]
  H --> O["--patch overlay"]

关键取舍:为什么是 CSS 覆盖,而不是替换组件。Web 客户端的插槽表里没有「品牌 logo」这个槽位,最近的是整块 sidebar——为了换一张图去接管整个侧边栏,代价远大于收益。两个品牌 svg 各带一个稳定且全站唯一的 viewBox(0 0 182 24 与 0 0 23.16 17.04),用它当选择器是耦合最小的做法。

规则本身只有两条:svg 自己贴背景图,svg[viewBox=…]>* 把原有图形藏掉。藏子元素而不是藏 svg,是为了让 svg 的宽高继续为布局占位,换图时周围元素不跳动;选 >* 而不是 >path,是因为品牌整块由 path / g / rect 多种元素拼成。品牌整块是一条 182×24 的横幅,自定义图片几乎不可能是这个比例,所以它用 background-position: left center 靠左对齐——右侧多出的空白落在 .brand 这个 flex: 1 的按钮里,不会挤动右边的折叠按钮;鱼形标记那处的框近乎方形,图片居中最自然,用的是 center。这也是「两处各给一张图」比「一张图管两处」好的原因:一条横幅塞进方形框里会缩得很小,一个方形标记铺进横幅框里则只占左侧一小截。

后续两层

这一节记录的是已经查清的落点,不是承诺的排期。两层的性质完全不同:模型配置几乎不需要代码,认证则被上游的扩展点挡住。

模型配置:纯声明,不写代码

dsh 的模型接线全在 patch 行里,DeepWorks 这一层要做的只是覆盖 base 层已有的几行——所以它不会是 insert: 的新插件,而是 cordis.patch.yml 顶层的几条覆盖:

行 idbase 层的现状DeepWorks 要定的
agent-default-modelprovider: deepseek-official / model: deepseek-v4-flash新会话默认落在哪个 provider 与模型
llm-deepseek原生 DeepSeek adapter,key 与 endpoint 不内联,每次请求从 settings 解析走 DeepWorks 网关时改 endpoint
llm-pi-ai多 provider 孪生,空载挂着:没有 provider profile 就零路由如果 DeepWorks 网关是 OpenAI 兼容的,provider profile 写在这里

要紧的是分清两个层次:patch 层是组合期的默认,$DSH_HOME/settings.yaml 才是运行期的真相(热重载,Web 的 Models 页写的就是它),而密钥属于第三层——credentials 行的 dsh-credentials-local 按「继承的环境变量 > $DSH_HOME/.credentials.yaml > 项目/用户 .env」解析,adapter 每次请求现取。所以本包可以定 endpoint 和默认模型,但不该把任何 key 写进包,只该声明它从哪个环境变量读。

登录认证:目前没有 seam

先说结论:这一层不能靠装一个插件解决。ctx.webServer 只有四个扩展点——register(具名路由)、registerUpgrade、registerFallback(只能注册一次,已被 SPA 静态服务占用)、tapIndex——没有中间件链。插件能注册自己的路由,但拦不住别人的:/api 的 HTTP 桥接与下行 WebSocket 属于 connection 插件的路由,dist 属于 fallback 持有者。webserver 自己的文档也把这点写在了「已知限制」里:不提供 TLS、认证或来源策略,绑非回环地址就是把服务暴露给对应网络。上游目前也没有账户概念——identity/ 那一层明说「这些值不表示经过身份验证的账户」,匿名 id 只是遥测关联用的 UUID。

于是只有两条真路径:

  1. 前置反向代理(推荐,零上游改动):dsh 只绑 127.0.0.1,nginx / oauth2-proxy 之类在前面做认证与 TLS,认证通过才转发。本包在这条路径上能做的是可选的锦上添花——比如把代理透出的用户名渲染进页面。
  2. 给上游加 seam:在 webserver 上开一个中间件/路由包装的扩展点,再由 dsh-deepworks/auth 注册。这是改上游,需要走 harness 那边的评审,好处是认证成为组合的一部分而不是部署的一部分。

一条看起来可行但别单独用的岔路:本包自己注册 /login 页面和签发 cookie,却没法校验 /api 上的每个请求——那是半套认证,比没有更危险。

开发

pnpm install       # 装依赖,并通过 prepare 跑一次构建
pnpm run build     # tsdown:两个入口 → lib/index.js + lib/branding/index.js(各带 .d.ts)
pnpm run typecheck
pnpm run test      # 冒烟 + 集成,都不需要浏览器和 API key
pnpm pack          # 产出可 dsh plugin add 的 tarball

tests/smoke.ts 用一个假的 ctx(只提供 webServer、logger、effect)直接跑 apply,快且不起进程,覆盖两张图各走各的路由、只换 brand 时图标仍取 mark、远程地址不挂路由、标题替换与转义、以及四类加载期校验;tests/integration.ts 则 new Context() 挂上真的 @deepseek-ai/dsh-host-webserver 和品牌层,用 fetch 打真实端口逐条比对字节,并在卸载 fiber 后确认两条路由都变成 404、index taps 也撤干净了。这份可测性来自「插件只依赖 ctx 上的服务」。

依赖只有三样,各有各的角色:@deepseek-ai/schemastery 是唯一的运行时依赖(配置 schema 真的会执行);@deepseek-ai/cordis 和 @deepseek-ai/dsh-host-webserver 都只被 import type,运行期不产生 import,所以是 devDependency——运行期的依赖声明是插件导出的 inject = ['webServer'],由宿主满足。

本包由 dsh-logo-swap 0.2.0 改名重组而来:那个包是通用的「换掉 logo」,这个包是「DeepWorks 的适配层」,图与标题都已内置,版本号因此重新从 0.1.0 起算。

发布到 npm

包名不带 scope,就叫 dsh-deepworks。

pnpm run typecheck && pnpm run test   # 发布是不可撤销的,先让两道闸都绿
npm version patch                     # 或 minor / major:改 package.json 并打一个 git tag
npm login                             # 一次即可,凭据存在 ~/.npmrc
pnpm publish --access public          # prepare 会在打包前自动跑一次 tsdown

pnpm publish 打包前会跑 prepare,所以发出去的一定是当次源码构建的产物,不会是本地残留的旧 lib/。tarball 里只有 files 列的 lib/、assets/、cordis.patch.yml,加上 npm 总会带上的 package.json、README.md、LICENSE——src/、tests/ 和构建配置都不在内。想先确认,用 pnpm publish --dry-run --no-git-checks,或 pnpm pack 出 tarball 后装到一个空目录里 import 一遍。

两件容易忘的事:npm 上的版本不能覆盖,发错了只能再发一个版本号(npm unpublish 有 72 小时窗口且会留下墓碑);发布的 package.json 里会原样带着那条指向本地 checkout 的 link: devDependency,消费者不装 devDependencies 所以无害,但 clone 仓库的人会踩到,见「已知限制」第一条。

已知限制

  • 和 harness 的版本是硬耦合的。 npm 上目前发布的 @deepseek-ai/dsh-host-webserver@0.0.1-rc.1 提供的服务名还是 httpServer,而本包按当前 harness 的 webServer 写。所以 package.json 里那条 devDependency 指向本地 checkout(link:../deepseek-harness/packages/host/webserver),克隆本仓库的人需要把 harness 放在同级目录,或改成一个已发布且服务名匹配的版本。harness 处于 pre-release,明确不承诺兼容旧格式,分发本包时要说明它对应哪个 dsh 版本。
  • viewBox 是与上游组件的唯一耦合点。 上游若调整品牌图形的画布尺寸,src/branding/index.ts 里的两个常量要跟着改,否则选择器静默失配——插件照常加载,只是什么都没换掉。
  • 只换图,不能换成自定义组件。 那需要 packages/client 侧真有一个品牌插槽(ctx.slots.register),属于要改上游的改动。
  • 品牌只覆盖到 index.html 这一层。 PWA 的 manifest.webmanifest(名称与图标)、favicon.svg 本体、以及界面里出现的产品名文案都还是上游的;manifest 可以用一条同名 exact 路由顶掉——具名路由胜过 dist 的 fallback——但那是下一步,现在没做。
  • 远程地址不在插件的掌控内。 写 https:// 的图由浏览器直接去源站取,源站挂了、断网、或站点将来收紧 CSP 的 img-src,图就加载不出来,而插件这边一切正常、无从报错。要确定性就用本地文件。