dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
🎮 DeepSeek Harness Web 端小游戏合集:俄罗斯方块 / 贪吃蛇 / 2048 / 扫雷,一个可拖动毛玻璃悬浮窗,支持深浅色主题与各游戏独立排行榜,一条命令安装。实测 @deepseek-ai/dsh 0.1.2-rc.1。
在 DeepSeek Harness Web GUI 上运行的小游戏合集:一个毛玻璃悬浮窗,内含 7 个游戏,可随时切换、拖动、收起。
| 游戏 | 玩法 | 排行榜 |
|---|---|---|
| 🕹️ 俄罗斯方块 | 7-bag 出块、墙踢旋转、消行计分、逐级加速、下一块预览 | 分数(高→低) |
| 🐍 贪吃蛇 | 15×15 网格,吃食物变长、撞墙/自撞结束、每 3 个加速一级 | 分数(高→低) |
| 🔢 2048 | 4×4 合并到 2048,键盘或屏幕方向键 | 分数(高→低) |
| 💣 扫雷 | 初级 9×9/10 雷、中级 12×12/24 雷,左键翻开 / 右键标记,首点安全 | 用时(低→高) |
| 🧱 打砖块 | 300×200 弹球台,7×4 砖墙、挡板控角、逐关加速与奖励分、三条命 | 分数(高→低) |
| 🀄 五子棋 | 13×13 棋盘,你执黑先手,启发式白棋 AI(会连五也会封四),可悔棋 | 用时(低→高) |
| 🃏 记忆翻牌 | 5×4 共 10 对,翻错 0.65s 自动盖回 | 用时(低→高) |
共性:
▁/✕ 收起为标题条(自动暂停),□ 还原| 组件 | 版本 |
|---|---|
@deepseek-ai/dsh(Desktop / Web GUI) | 0.2.1-rc.2(当前安装版,已实测) |
| 前一版 | 0.2.0-rc.2(同一套 API,仍兼容) |
| 插件形态 | profile bundle(dsh.bundle.patch + dsh.client),经 Plugin Manager 安装 |
| 客户端硬依赖 | slots(两个槽位)、timer(各游戏时钟)、平台模块 react |
| 占用槽位 | conversation.session.header.utilities(list / session)、shell.overlay(list / root) |
| 已不再使用 | cordis_define / cordis_run(见下) |
📌 0.2.1 的槽位契约(取自宿主自带的 slot catalog,
cordis_inspect_query同源):conversation.session.header.utilities kind: list scope: session replaceRisk: none options: id(required) / order / label shell.overlay kind: list scope: root注册形态与该 catalog 的官方示例逐字一致,本包不需要任何迁移代码。
smoke / render-check 覆盖全部 7 款游戏;locale 卡片描述同步为七款。对着已安装的 0.2.1-rc.2 逐项核对后,结论是:0.4.0 的运行时用法本来就兼容 0.2.1,真正过时的是"另一条安装通道"和文档。因此这一版做的是收口与验证,而不是改游戏逻辑:
cordis_define 通道。0.2.1 的 @deepseek-ai/dsh-tool-cordis 只暴露 cordis_inspect_list / cordis_inspect_query,模型侧已经没有 cordis_define / cordis_run;dist/cordis-plugin.js、dist/cordis-plugin.code.txt、scripts/smoke-cordis.mjs 一并删除。bundle 现在是唯一安装路径。scripts/check-host-contract.mjs:直接读取已安装的 app.asar(自带 asar 解析,不需要 Electron),核对两个槽位的 kind/scope、timer 服务、平台模块表里的 react、以及 dsh.client.inject 里每个包是否还在。DSH 升级后跑一次就知道有没有漂。npm run smoke 扩展为清单校验:cordis.patch.yml 的裸包名行、dsh.client.platform/immediately/inject、exports["./client"]、locale/*.json 的 meta.title/description、icon.svg ≤ 256 KiB。package.json 增加 dsh.compatibility.dshReleases:明确标注 0.2.0-rc.2 / 0.2.1-rc.2 为 compatible(Plugin Manager 的卡片用它显示兼容性)。lib/ 与 scripts/build-lib.mjs(旧的内联归档)、exports["./payload"]、cordis_define 关键词。npm run dev 预览修正:替身外壳把 conversation.session.header.utilities 的注册显示在侧栏工具条里(真实宿主里它在会话标题栏右上角)。在 GUI 里:设置 → 插件 → 添加插件,输入 github:LeeGuanWei-a/dsh-mini-games-lee。
或让模型调用:
plugin_manager({ action: "install_bundle", target: "github:LeeGuanWei-a/dsh-mini-games-lee" })
等价的 CLI(推荐给 desktop / web profile):
dsh plugin --profile desktop add github:LeeGuanWei-a/dsh-mini-games-lee
✅
github:owner/repo、https://github.com/owner/repo、registry 包名、.tgz都可以;它们都会在node_modules里得到真实目录副本。❗ 不要用本地路径装(
target: "F:\\Projects\\dsh-mini-games-lee")。那会产出link:依赖,profile 里的目录是个 junction;Node 会把它解析到真实位置,而本包的裸包名行在真实位置之上解析不到,Loader 拿不到 fiber,install_bundle报failed to import。git / registry / tarball 规格才是受支持的安装方式(@deepseek-ai自带的 bundle 也是这么装的)。
安装后 profile 的 dsh.profile.bundles 会登记本包,cordis.patch.yml 插入的那一行由 Loader 挂载,浏览器半区通过包清单里的 dsh.client 被发现。
什么时候生效:Plugin Manager 在 0.2.1 里会按 profile 是否带 HMR 决定。带 dsh.profile.patchReload: live 的 profile 当场重算;当前 desktop profile 没开 HMR,所以安装后会得到 restart-required,重启 DeepSeek Harness 后会话标题栏右上角出现 🎮。
📍 🎮 的位置:
conversation.session.header.utilities(会话标题栏右对齐工具条,即页面右上角),order: 20排在自带条目之后——实测紧挨在右侧栏开关左边。 这一槽是 Session 作用域:空白 Hero 页没有标题栏,也就没有 🎮;但游戏窗口本身在 root 作用域的shell.overlay里,已经打开的窗口不受影响。 没有选隔壁的conversation.session.header.corner:那是single且已被自带控件占用,注册进去会顶掉那个控件。
npm install
npm run build
npm run dev # http://127.0.0.1:5188/ 的替身外壳,4 个游戏都能玩
DSH 的 bundle 是一个包、两个半区,两者互不 import:
profile 的 dsh.profile.bundles
│
├─ cordis.patch.yml ──► insert 一行
│ id: dsh-mini-games-lee
│ name: dsh-mini-games-lee ← 裸包名,必须是裸包名
│
└─ package.json 的 dsh.client
platform: "web" ← 声明这是 web 客户端插件
immediately: true ← 开机即激活,不等 Session
inject: [ui-sidebar, ui-layout] ← 只影响到达顺序
│
└─ exports["./client"] ──► dist/client.js ← 浏览器半区
Host 半区 dist/index.js 只导出空的 apply():它唯一的职责是让这一行在 Loader 里激活。浏览器半区 dist/client.js 才是全部逻辑,以 Client 模块表的 window.__ModuleLoader__.load({ id, factory }) 形式注册。
平台模块表(0.2.1 实测):壳层冻结的 seed 只有
react、react/jsx-runtime、react-dom、react-dom/client、@deepseek-ai/cordis、zod、@tanstack/react-virtual、@deepseek-ai/dsh-client-store、@deepseek-ai/dsh-client-ui-slots、@deepseek-ai/dsh-client-ui-primitives、@deepseek-ai/dsh-client-ui-dockkit。 本包只require("react"),所以不需要dsh.client.external(那个字段只用于非基座请求)。
⚠️
name必须是裸包名dsh-mini-games-lee,不能用相对路径 —— 两件事都靠它:① 解析。 bundle patch 插入的行以bundle 自己的目录为解析基准,不是 profile。从
node_modules/dsh-mini-games-lee/向上一级就能命中包名。唯一会失败的情况是link:/ 本地路径安装。所以要用 git / registry / tarball 规格安装。② 显示元数据。
@deepseek-ai/dsh-app-boot的readPluginMeta第一行就是if (barePackageName(specifier) === void 0) return void 0。行名写成路径 → 元数据整块放弃:Plugin Manager 退回把解析后的模块 URL 当作组件名打印,本包的icon.svg和locale/*.json里的标题/描述一个都不会被读。裸包名是这一切的前置条件。
窗口顶部是一排 游戏标签,点击切换(切换会开始新的一局)。
| 操作 | 按键 / 屏幕按钮 |
|---|---|
| 左右移动 | ← / →,或 ← → |
| 软降(+1/格) | ↓,或 ▼ |
| 旋转(带墙踢) | ↑ 或 X,或 ↻ |
| 反向旋转 | Z |
| 硬降(+2/格) | 空格,或 ⤓ |
| 暂停 / 继续 | P,或 Ⅱ |
| 操作 | 按键 / 屏幕按钮 |
|---|---|
| 转向(反向输入被忽略) | ← → ↑ ↓ 或 WASD,或 ↑ ← ↓ → |
| 暂停 / 继续 | 空格 或 P,或 Ⅱ |
| 操作 | 按键 / 屏幕按钮 |
|---|---|
| 滑动合并 | ← → ↑ ↓ 或 WASD,或 ↑ ← ↓ → |
| 暂停 / 继续 | P,或 Ⅱ |
| 操作 | 按键 / 屏幕按钮 |
|---|---|
| 翻开格子(首次点击才布雷,该格及周围保证无雷) | 鼠标左键 |
| 插旗 / 取消插旗 | 鼠标右键 |
| 切换难度(初级 9×9 · 中级 12×12) | 初级 / 中级 |
| 重新开始 | ↺ 新局 |
| 操作 | 按键 / 屏幕按钮 |
|---|---|
| 左右移动挡板 | 按住 ← / →(或 A / D),或按住 ‹ › |
| 发球 | 空格,或 发球 |
| 暂停 / 继续 | P 或 空格(球已在飞时),或 Ⅱ |
| 重新开始 | ↺ |
| 操作 | 按键 / 屏幕按钮 |
|---|---|
| 落子(你执黑先手,之后白棋 AI 自动应一手) | 鼠标点击空交叉点 |
| 撤销一整回合(你 + AI 各一手) | 悔棋 |
| 重新开始 | ↺ 新局 |
| 操作 | 按键 / 屏幕按钮 |
|---|---|
| 翻开一张牌(同时翻开两张后判定,翻错 0.65s 盖回) | 鼠标点击牌面 |
| 重新开始 | ↺ 新局 |
dsh-mini-games-lee/
├── package.json # bundle 清单:dsh.bundle.patch + dsh.compatibility + dsh.client + exports
├── cordis.patch.yml # 插入 Host 行(name: dsh-mini-games-lee,裸包名)
├── icon.svg # Plugin Manager 卡片图标(package.json 的 icon 字段)
├── locale/
│ ├── en.json # 卡片显示元数据:meta.title / meta.description
│ └── zh.json # 语言文件必须叫语言 id(本宿主内置的只有 zh / en)
├── src/
│ ├── index.js # Host 半区:空 apply(纯 UI 插件)
│ ├── client.js # 浏览器半区入口:游戏注册表 + apply
│ ├── shared/
│ │ ├── theme.js # 深浅色板
│ │ ├── records.js # 按游戏分库的 localStorage 排行榜
│ │ ├── store.js # 窗口状态(open/theme/pos/game)
│ │ └── ui.js # 悬浮窗框架:拖拽、游戏标签、通用排行榜、入口按钮
│ └── games/
│ ├── tetris.js # 🕹️ 俄罗斯方块
│ ├── snake.js # 🐍 贪吃蛇
│ ├── g2048.js # 🔢 2048
│ ├── minesweeper.js # 💣 扫雷
│ ├── breakout.js # 🧱 打砖块
│ ├── gomoku.js # 🀄 五子棋
│ └── memory.js # 🃏 记忆翻牌
├── dist/ # 构建产物(`npm run build` 生成)
│ ├── index.js # Host 半区 —— Loader 挂载的就是它
│ └── client.js # 浏览器半区 —— exports["./client"]
└── scripts/
├── inline.mjs # 【关键】模块顺序 + import/export 剥离,唯一的平铺作用域权威
├── build-cordis.mjs # 唯一的构建脚本:平铺体 → dist/index.js + dist/client.js
├── smoke.mjs # 按模块表方式加载 dist/client.js + 校验 bundle 清单
├── render-check.mjs # 真实 React 渲染每个组件(深浅两色)
├── check-host-contract.mjs # 对着已安装的 app.asar 核对槽位/服务/平台模块
└── dev-cordis.mjs # 本地替身外壳,肉眼确认 4 个游戏可玩
在 src/games/ 新建模块,导出一个游戏对象:
export const MYGAME = (() => {
function View(props) { // props: { timer, th, active, onGameOver, onOpenRank }
// 用 el(...) 渲染;props.th 提供当前主题色;
// props.active 为 false 时不要跑时钟/键盘;
// 需要时钟就用 props.timer.interval(fn, ms),并在 effect 里返回它的 disposer;
// 一局结束时调用一次 onGameOver({ value: 分数, meta: '说明文字' })
}
return { id: 'mygame', title: '我的游戏', icon: '🎯', short: '短名', scoreOrder: 'desc', View }
})()
在 src/client.js 的 GAMES 数组里加入它;
在 scripts/inline.mjs 的 MODULES 里把新文件加在 src/client.js 之前(顺序 = 扁平作用域里的依赖顺序);
npm run check 校验,然后 npm run build 重新生成 dist/;
重新装一次(git / registry 规格),让 Loader 拿到新的模块代与 patch。
npm install
npm run check # build + smoke + render-check
npm run build —— 把 scripts/inline.mjs 列出的模块按依赖顺序拼成单一扁平作用域,再包成 Host 半区与模块表浏览器半区npm run smoke —— 按模块表方式加载 dist/client.js,断言两个槽位以正确的 id 注册、apply 交回 disposer、缺 timer 服务时保持惰性;并校验 bundle 清单(patch 行 / platform / immediately / exports / locale / icon)npm run render-check —— 用 real React + react-dom/server 真实渲染每个注册的组件(深浅两色),抓"能注册但一渲染就崩"的故障npm run dev —— 起本地替身外壳(默认 http://127.0.0.1:5188/),肉眼确认 4 个游戏都可玩升级 DSH 之后跑一次,确认槽位/服务/平台模块还在:
npm run build
npm run check:host -- --asar "E:\SoftWare\DeepSeek Harness\resources\app.asar"
输出(0.2.1-rc.2 实测):
ok - host runtime: @deepseek-ai/dsh 0.2.1-rc.2 (…\app.asar)
ok - slot "conversation.session.header.utilities" declared list/session in @deepseek-ai/dsh-client-ui-conversation
ok - slot "shell.overlay" declared list/root in @deepseek-ai/dsh-client-ui-layout
ok - service "timer" (interval/timeout/…) provided by cordis-plugin-timer
ok - platform-module seed table exposes "react"
ok - dsh.client.inject @deepseek-ai/dsh-client-ui-sidebar present (0.2.1-rc.2)
ok - dsh.client.inject @deepseek-ai/dsh-client-ui-layout present (0.2.1-rc.2)
ok - manifest: main + exports["./client"] point at built halves
也可以指到一个装着 @deepseek-ai/* 包的目录:--dsh-root <dir>/node_modules。不给参数时它只打印一行 skip,不会失败。
⚠️ 不要把
src/交给 esbuild 之类的 ESM 打包器。src/是"扁平共享作用域"写法:模块之间互相引用el、useStore、THEMES等声明而不写 import,模块顺序由scripts/inline.mjs的MODULES决定。真 ESM 打包会给每个模块单独作用域,于是useStore is not defined;而且 esbuild 会把el改名成el2却漏改其它模块里的el(...)调用点,于是el is not defined。两个故障都实测出现过。新增/移动模块时改MODULES顺序即可。
dsh.minigames.records.v1.<gameId>;plugin_manager({ action: "remove_bundle", target: "dsh-mini-games-lee" })
或:
dsh plugin --profile desktop remove dsh-mini-games-lee
MIT。