modelbus/deepseek-harness-pro

插件Plugin ⭐ 3 编程Coding

deepseek-harness-pro 是基于 deepseek-harness 的 Web+Electron 客户端,兼容已有的deepseek-harness环境,并支持一键部署最新版deepseek-harness。相比原web功能做出增强:新增实时任务看板、电脑管家(清理/调优/进程管理)、独立插件中心等功能。界面友好,跨平台,开源免费,让 deepseek-harness 更强大易用。

项目介绍Project Overview

deepseek-harness-pro 是 DSH 宿主插件仓库,将 upstream deepseek-harness 作为 git submodule 引入,并以 app/web 独立前端覆盖 upstream 的 apps/web,使 pnpm dsh web 自动加载宿主 dist。核心能力包括 pnpm run build 重新打包 upstream 并用宿主 dist 覆盖、pnpm dsh-pro web 启动定制入口,以及 app/* 下的 Cordis 插件通过 symlink 自动注入 profile。适用场景:在 DSH 上做宿主级 Web 定制或开发 Cordis 插件并发布为 npm 包。警示:克隆时若未加 --recurse-submodules 需运行 pnpm run setup 补全子模块;子模块内禁止直接修改,依赖更新由 upstream Dependabot 负责。

deepseek-harness-pro is a DSH host repository that tracks upstream deepseek-harness as a git submodule and replaces its apps/web with a self-contained app/web frontend, so pnpm dsh web automatically loads the host-built dist. Core capabilities include pnpm run build to rebuild upstream and overwrite its dist with the host frontend, pnpm dsh-pro web to launch the customized entry, and symlink-based auto-injection of app/* Cordis plugins into the dsh profile. Use it for host-level web customization or developing and publishing Cordis plugins. Caveat: clone with --recurse-submodules or run pnpm run setup; do not edit inside deepseek-harness/, since dependency updates are managed by upstream Dependabot.

或使用命令行安装(适合开发者)Or use CLI install (for developers)

命令行安装CLI Install

dsh plugin --profile web add github:modelbus/deepseek-harness-pro

modelbus/deepseek-harness-pro 加入你的 DSH 配置(web profile)即可启用。

READMEREADME

deepseek-harness-pro

宿主仓库,跟踪 deepseek-harness 作为 git submodule,并以独立 web 前端 (app/web) 替换 upstream 的 apps/web,使得 pnpm dsh web 自动加载本仓的 web 产物。

deepseek-harness/ 子目录的内容来自 upstream 仓库,不要在此目录内直接修改。本仓不复制 upstream 任何源码:宿主 web 仅含一个 main.ts 入口和一份 vite 配置;web shell 本身来自 upstream 的 @deepseek-ai/dsh-client-web(通过 vite alias 解析到 submodule 的 packages/client/web/src/index.ts,是该包 ./src/* exports 明确允许的用法)。

首次克隆

git clone --recurse-submodules https://github.com/<your-org>/deepseek-harness-pro.git
cd deepseek-harness-pro
pnpm install          # 或 npm install / yarn install

--recurse-submodules 拉取 deepseek-harness/ 子目录。pnpm install 完成三件事:

  1. preinstallscripts/setup.sh → 必要时 git submodule update --init --recursive + 给 submodule 装依赖
  2. 安装根 dev-only 工具(暂无)
  3. 安装 workspace member app/web 的依赖(vite、react、...)

如果克隆时忘了 --recurse-submodulesscripts/setup.sh 仍会检测到空子模块目录并主动 git submodule update --init --recursive deepseek-harness,所以两种克隆方式都能正常工作

pnpm-lock.yaml 在根目录生成。app/web/node_modules 是 pnpm 链接,不要单独 pnpm install 它。

包管理器

package.json 是 pnpm workspace 声明(pnpm-workspace.yaml),所以 pnpm 是首选且功能完整。yarn / npm 也能跑 pnpm install 的等价流程(scripts/setup.sh 会自动检测并调用),但 yarn/npm 不能跑 pnpm --dir deepseek-harness dsh webpnpm dsh 是 pnpm 的 workspace CLI shortcut)。

为了在三种包管理器下都能用,仓库额外提供一个 scripts/run.sh 包装:

./scripts/run.sh install   # 等价于 setup.sh + pkg_manager install
./scripts/run.sh web       # 等价于 pnpm run web
./scripts/run.sh build     # 等价于 pnpm run build(见下)
./scripts/run.sh dsh:web   # 等价于 pnpm run dsh:web(要求 pnpm)

检测顺序:pnpm > yarn > npm,由 scripts/detect-pkg-manager.sh 决定。

常用脚本

pnpm run web          # 启动 app/web 的 Vite dev server(默认 http://localhost:5173)
pnpm run web:build    # 一次性:app/web 生产构建到 app/web/dist/
pnpm run build        # 完整发布构建:upstream dsh + 宿主 web 覆盖
pnpm run build:web-only   # 仅重建宿主 web 并覆盖到 submodule 的 apps/web/dist
pnpm run dsh:web      # = `pnpm dsh web`:跑已 build 的 upstream dsh,自动加载所有 app/* 插件
pnpm run dsh-pro:web  # = dsh:web(语义别名:宿主定制的 dsh 入口)
pnpm run dsh-pro      # = upstream `pnpm dsh`(不指定 profile,默认 headless)
pnpm run demo:bundle  # 打包 app/demo-cordis 示例插件
pnpm run demo:typecheck   # app/demo-cordis 类型检查
pnpm run demo:pack    # bundle + 出 tarball(准备发布)
pnpm run ui-tweak:bundle  # 打包 app/ui-tweak 插件
pnpm run apps:link    # 手动触发 symlink(preinstall 默认跑)
pnpm run profile:sync # 手动把 app/* bundles 写进 dsh profile dsh.profile.bundles
pnpm run setup        # 仅跑 scripts/setup.sh

pnpm run build 的精确语义:

  1. pnpm --dir deepseek-harness run build —— upstream 完整发布构建:build 所有 host packages、tsdown 出 apps/cli/lib/bin.jsbuild:web 产出 apps/web/dist/,并写 client-build-record
  2. pnpm --filter @deepseek-ai/dsh-web-frontend run build —— 宿主 app/web vite build,输出 app/web/dist/
  3. rm -rf deepseek-harness/apps/web/dist && cp -R app/web/dist/. deepseek-harness/apps/web/dist/ —— 用宿主 dist 覆盖 submodule 的 dist。
  4. 此时 require.resolve('@deepseek-ai/dsh-web-frontend/dist/index.html')(由 packages/bundle/web-app 触发)指向宿主 dist;pnpm dsh-pro web 自动加载宿主 web。

build:web-only 跳过步骤 1,仅做步骤 2-3。修改上游 client 代码后想要完整重打 dsh 产物,跑 pnpm run build

pnpm dsh-pro webpnpm dsh web 的区别仅在名字:都调用 deepseek-harness/apps/cli/lib/bin.js(由步骤 1 产出),并且因为步骤 3 的覆盖,web profile 注入的 __DSH_BOOT__ 服务的是宿主 dist。这是宿主定制的 dsh 入口;当未来 app/cli 真正承载 dsh 的二次开发时,再把脚本切到本地 app/cli/lib/bin.js

独立的 web 前端 (app/web/)

app/web/ 是本仓唯一的自有 web 前端。源文件树:

app/web/src/
├── main.ts               Vite entry: import global.css + runApp()
├── mount.ts              find #root, instantiate AppWebEntry
├── bootstrap.ts          host customization hooks (customSeams)
├── env.d.ts              Vite + CSS ambient types
├── node-module-stub.ts   browser stand-in for node:module
├── styles/global.css     host-level global stylesheet
└── types/dsh-client-web.d.ts   ambient module declaration for typecheck

mount.ts 是手写入口:

import { AppWebEntry } from '@deepseek-ai/dsh-client-web'
import { customSeams } from './bootstrap.ts'

export function runApp(): Promise<void> {
  const container = document.getElementById('root')
  if (container === null) throw new Error('app/web: missing #root element')
  const entry = new AppWebEntry(container, customSeams)
  return entry.run()
}

@deepseek-ai/dsh-client-webapp/web/vite.config.ts 中通过 resolve.alias 解析为 deepseek-harness/packages/client/web/src/index.tstsconfig.jsonpaths 把它指向本地 src/types/dsh-client-web.d.ts ambient stub,让 host 端 tsc 不必顺着 submodule 源码做整库类型检查。

包名 @deepseek-ai/dsh-web-frontend 与 upstream apps/web 同名,因此 dsh web 通过 require.resolve 加载到的就是宿主构建的 dist。app/web/README.md 详细说明在哪里做二次开发(bootstrap.tsstyles/global.cssmain.tsenv.d.ts)。

关于 standalone dev

upstream deepseek-harness/apps/web/vite.config.ts 故意拒绝 vite dev(必须有 host 注入 window.__DSH_BOOT__)。app/web/vite.config.ts 移除了这个守卫,因此 pnpm run web 可以在没有 host 的情况下启动 Vite dev server,仅渲染 boot page(<AppWebEntry> 启动后立即因缺少 window.__DSH_BOOT__ 而停下)。要让 plugin 真正加载,需要另一终端:

pnpm run dsh:web   # 自动加载所有 app/* 插件:监听 webserver、注入 __DSH_BOOT__、serve 我们的 dist

示例插件 (app/demo-cordis/)

app/demo-cordis/ 是一个宿主侧 Cordis 客户端插件示例,向 dsh web 注入三个 slot 注册:

Slot 类型 行为
sidebar.footer.action list (id) "演示/Demo" 按钮 → 显示当前 session 数量的 Modal
conversation.hero.workspace single (id) 占位 hero(conversation 空状态时渲染)
tool.call.toolview keyed(generator 双 yield) demo-approve / demo-decline 两个原子注册

启动(开发循环)

pnpm install                       # 注册 workspace + symlink 插件 + 注入 cordis.patch.yml
pnpm run build                     # 一次性:upstream dsh build + 宿主 web 覆盖(首次必需)

# 然后开两个终端:
# 终端 1
pnpm demo:watch                    # tsdown watch 模式,src/ 变了自动重建 lib/

# 终端 2
pnpm dsh:web                       # 零参数启动,自动加载所有 app/* 插件

浏览器打开 http://127.0.0.1:4567(默认 web 端口),侧边栏底部出现 "演示/Demo" 按钮。

后续迭代插件:编辑 app/demo-cordis/src/client/* → tsdown 自动重建 → 浏览器刷新即可。

加一个新的 host-side 插件

  1. mkdir app/<my-plugin> && cd app/<my-plugin> —— 复制 app/demo-cordis/ 的结构。包名 scope 用 @dhs-pro/(deepseek-ai 是官方 scope,不要占用),例如 @dhs-pro/my-plugin
  2. 编辑 package.json
    • name@dhs-pro/my-plugin
    • dependenciesnpm:^0.1.1-rc.1(已发布的 @deepseek-ai/dsh-client-*),link:
    • peerDependencies@deepseek-ai/cordis: "*"(dsh 安装自带)
    • dsh.client.platform: "web"dsh.bundle.patch: "./cordis.patch.yml"(已默认)
  3. 编辑 cordis.patch.yml(不是 cordis.yml)—— - id: my-plugin / name: '@dhs-pro/my-plugin'
  4. 在根 pnpm-workspace.yamlpackagesapp/<my-plugin>
  5. pnpm install —— preinstall 自动 symlink 到 ~/.dsh/profiles/web/node_modules/@dhs-pro/my-plugin,并把包名 append 到 dsh.profile.bundles
  6. pnpm --filter @dhs-pro/my-plugin run bundle 产出 lib/
  7. pnpm dsh:web —— 零参数启动,新插件自动加载(dsh 自己的 loadProfiledsh.bundle.patch 把 patch 应用到 layer stack)

新插件加完 cordis.patch.yml 后零配置:下次 pnpm install + pnpm dsh:web 自动加载。

发布到 npm 的版本:去掉 private: true,加 "publishConfig": { "access": "public" },跑 pnpm <my-plugin>:pack 出 tarball,pnpm publish --filter @dhs-pro/my-plugin。用户只需 dsh plugin --profile web add @dhs-pro/my-plugin 就装上——dsh 自己的 reconcile 把 bundle + client 两侧都加载。详见 docs/plugin-guide.md §12

关于 symlink 与 patch 注入(自动化)

scripts/setup.shpreinstall 钩子里依次调用:

  1. scripts/link-app-workspaces.sh — 把所有 app/*/package.json#name symlink 到 ~/.dsh/profiles/web/node_modules/<name>。跳过 app/web(它走 dist 覆盖路径,不需要 Loader 解析)。Scope 从 package.json 推断,@dhs-pro/<pkg>node_modules/@dhs-pro/<pkg>

  2. scripts/sync-profile-bundles.sh — 扫描 app/*/package.json,收集所有声明了 dsh.bundle.patch 的包名,追加~/.dsh/profiles/web/package.json#dsh.profile.bundles(不动 upstream 自带的 @deepseek-ai/dsh-base 等)。这与 dsh 自己 dsh plugin add 的 reconcile 流程对齐——host repo 借用同一条官方加载路径。

profile 目录还不存在(首次运行)时 link + inject 是 no-op:先 pnpm dsh:web Ctrl-C 让 dsh 自动初始化 profile,再 pnpm install 即可。

UI 主题 / 特效插件 (app/ui-tweak/)

app/ui-tweak/ 是一个宿主侧 UI 定制示例:把 CSS 风格改写和 JS 特效作为 Cordis 插件加载,覆盖整个 shell 的视觉与交互。可作为二次开发 web UI 的起点。

它做了什么

  1. 主题 token 改写 — 启动时(默认 midnight)调用 document.documentElement.style.setProperty('--dsw-*', ...),把当前主题 调色板写入 root。upstream shell 与所有 plugin 读同一套 --dsw-alias-* token,整页立即重绘。Cmd+. / Ctrl+. 循环切换 default / midnight / sepia / candy 四套调色板。

  2. 全局 stylesheet 注入 — 在 <head> 末尾追加一个 <style id="dsh-tweak-sheet">,对 shell 结构类([class*='card'][class*='panel'] 等)做高优先级覆盖:圆角、阴影、scroll-driven parallax 视差(--dsw-tweak-scroll-y)、Konami 彩蛋动画。

  3. JS 特效

    • Cmd+. / Ctrl+. 全局切换主题
    • 滚动时写 --dsw-tweak-scroll-y:root,触发 CSS 视差
    • Konami code (↑↑↓↓←→←→BA) 触发 1.5s hue-rotate 闪光
  4. Sidebar 按钮 — 在 sidebar.footer.action 注册一个 ThemeButton,显示当前主题名 + 主题色点,点击循环切换。

启动

pnpm install                                       # preinstall 自动 link + 注入 cordis.patch.yml
pnpm --filter @dhs-pro/ui-tweak run bundle   # 一次性
pnpm dsh:web                                       # 零参数启动,加载所有 app/* 插件

不需要为某个具体插件再写一条 dsh:web:<name> 命令 —— 所有 app/*/cordis.yml 都由 scripts/sync-profile-bundles.sh 自动注册到 dsh profile 的 dsh.profile.bundles,dsh web 启动时自己通过官方 loadProfile 路径加载。

迭代开发:pnpm ui-tweak:watch 在终端 1 自动重建,pnpm dsh:web 在终端 2 运行;改 themes.ts / styleSheet.ts / effects.ts 任一即生效。

编辑主题/特效

想改什么 文件
调色板(颜色、键名) app/ui-tweak/src/client/themes.ts
全局 CSS 覆盖(圆角、阴影、视差、动画) app/ui-tweak/src/client/styleSheet.ts
键盘 / 滚动 / 彩蛋监听 app/ui-tweak/src/client/effects.ts
Sidebar 按钮 UI app/ui-tweak/src/client/ThemeButton.tsx + ThemeButton.module.css
Slot 注册与生命周期 app/ui-tweak/src/client/index.ts

局限(明确知道)

Cordis slot 系统是声明式且叠加的:插件可以新增 slot entry,但不能替换已注册部件。这意味着:

  • ✓ 可以加新按钮、新动画、新事件、新主题
  • ✓ 可以在自己注册的 component 内自由使用 CSS Module 改风格
  • ✓ 可以通过 CSS 变量 + 全局 stylesheet 改绝大多数 shell 视觉(因为 shell 读 --dsw-* token)
  • ✗ 不能直接覆盖 upstream shell 已注册的 component(如果某个菜单必须换样式,需要先在 effects.ts 里给对应节点 injectStylesetAttribute,或者开新 slot)

如需更深改造,下一步可:写一个 app/ui-shell-override/,里面把 ui-shell 已声明 slot 的整个 children 列出来作为 hook 点,逐个替换。

同步 upstream

git submodule update --remote deepseek-harness

这会把 deepseek-harness/ 更新到 upstream 最新 commit,然后在父仓里产生一个新的 gitlink 变更,需要单独提交:

git add deepseek-harness
git commit -m "chore(deps): bump deepseek-harness submodule"

升级到指定 commit / tag / branch

cd deepseek-harness
git checkout dsh-v0.1.1-rc.1   # 或某个 branch / commit
cd ..
git add deepseek-harness
git commit -m "chore(deps): pin deepseek-harness to dsh-v0.1.1-rc.1"

CI

.github/workflows/submodule-freshness.yml 每天 00:30 UTC 跑一次,比较 deepseek-harness 子模块当前 pinned SHA 与 upstream master。若落后则 fail 并提示重启命令。可手动 Actions → Submodule freshness → Run workflow 触发。

如果不再需要这个提醒,删掉 .github/workflows/submodule-freshness.yml 即可。

Dependabot

本仓启用 Dependabot。.github/dependabot.yml 已删除,避免它跨 submodule 扫描 deepseek-harness/package.json 产生孤儿 PR。子模块内的依赖更新由 upstream /deepseek-harness/.github/dependabot.yml 自己负责。

代理

如果所在网络访问 GitHub 需要代理,请在执行上述命令前导出代理环境变量:

export https_proxy=http://127.0.0.1:7890
export http_proxy=http://127.0.0.1:7890
export all_proxy=socks5://127.0.0.1:7890

也可在 ~/.gitconfig 中全局配置:

[http]
    proxy = http://127.0.0.1:7890
[https]
    proxy = http://127.0.0.1:7890

已知问题

install-lefthook.mjs 在 submodule 模式下 postinstall 失败

deepseek-harnesspackage.jsonpostinstall: node scripts/install-lefthook.mjs。在 submodule 模式下:

[install-lefthook] cannot enable extensions.worktreeConfig while core.worktree
is in the common config; move it to the main worktree config first

install-lefthook.mjs 想给子模块 .git/configextensions.worktreeConfig = true,但 git 拒绝,因为 core.worktree 还在 common config 区。所有 workspace 包都已装好,只有 lefthook 钩子没装上。

scripts/setup.sh 默认就以 --ignore-scripts 跑 submodule install,绕开这条 postinstall。如果想要 lefthook 钩子,可在子模块里手动把 core.worktree 提到 worktree 区:

cd deepseek-harness
git config --local --unset core.worktree
git config core.worktree "$(pwd)"
git config extensions.worktreeConfig true

ERR_PNPM_MINIMUM_RELEASE_AGE_VIOLATION on first install

pnpm >= 11 默认对未列入白名单的 dep 拒绝执行(minimumReleaseAge 默认 1 天)。vite 间接依赖 browserslist → electron-to-chromium,其版本更新频繁。本仓 pnpm-workspace.yaml 显式关闭了这两个 supply-chain check(verifyDepsBeforeInstall: false / minimumReleaseAge: 0),并附 README 说明如何临时重新启用。

目录结构

deepseek-harness-pro/
├── .gitmodules              # submodule 配置(HTTPS URL)
├── docs/                    # 本仓自有文档
├── app/
│   └── web/                 # 宿主 web 前端(@deepseek-ai/dsh-web-frontend)
│       ├── src/
│       │   ├── main.ts
│       │   ├── mount.ts
│       │   ├── bootstrap.ts
│       │   ├── env.d.ts
│       │   ├── node-module-stub.ts
│       │   ├── styles/global.css
│       │   └── types/dsh-client-web.d.ts
│       ├── README.md        # 二次开发指南
│       ├── vite.config.ts   # alias 全部 @deepseek-ai/* 到 submodule 源码
│       └── package.json
├── deepseek-harness/        # ← upstream 源码(git submodule)
├── scripts/
│   ├── setup.sh             # preinstall: init submodule + link 插件 + 注入 cordis.patch.yml
│   ├── detect-pkg-manager.sh
│   ├── link-app-workspaces.sh    # symlink app/* Cordis 插件到 dsh profile
│   ├── sync-profile-bundles.sh   # sync dsh.profile.bundles with all dsh.bundle.patch-declaring apps
│   └── run.sh                  # 包管理器无关的入口包装
├── docs/
│   ├── plugin-guide.md         # 1500+ 行 host-repo 插件开发手册
│   ├── plugin-development.md    # Cordis 内核 + dsh-base/web-app 内部细节
│   └── web-api.md              # pnpm dsh web 的浏览器↔Node HTTP 协议
├── package.json
├── pnpm-workspace.yaml
├── pnpm-lock.yaml
└── README.md

进一步阅读

  • 本仓 docs/plugin-guide.md — host-repo 插件开发手册(app/<pkg>/ 工作流、symlink、dsh.profile.bundles 同步、tsdown bundle、npm 发布)
  • docs/plugin-development.md — Cordis 内核机制 + dsh-base/web-app 内部细节
  • 上游 cordis-tutorial/ — 7 章从零跑教程(first plugin → into the harness),最贴近"我刚接触 Cordis"的入口

上一个 Prev dsh-polymarket-knowhow 下一个 Next npm-safe-forDSH