工作台开发规范

版本:2026-10-27 · 以官网版本为准,DSH Desktop 内附文档副本供离线取用。

官方地址:https://dshdesktop.com/workbench/docs/development/

Markdown 原文(供 Agent 读取):https://dshdesktop.com/workbench/docs/development.md

六步速览:https://dshdesktop.com/workbench/docs/quickstart/ (Markdown 原文)。

本文是开发工作台时由 AI 直接执行的规范。只在本地开发、自己使用时,满足本文即可:按第 3 节写好包,装到本机 DSH Desktop,按第 8 节自测通过,就可以正常使用,不需要公开代码,也不需要上传到任何地方。

如果之后想把工作台上架到工作台市场,再阅读《工作台市场验收规范》:https://dshdesktop.com/workbench/docs/market-acceptance/ 。仓库公开、发布、简介、截图和收录 PR 的要求都在那一份里,本文不涉及。

工作台是一种 DSH 插件。它首先必须是一个合格的 DSH 插件包,能被 dsh plugin add 安装、被 Harness 加载;在此基础上,再向 Desktop 注册业务面板,并遵守工作台与会话、工作区的交互规则。

规则分三级:必须(不满足就不能安装或加载)、建议(强烈推荐,不满足会影响体验或以后上架)、可选。

1. 执行原则

规范获取顺序:开始开发前必须实际获取并阅读全文上述官网 Markdown,记录获取地址和文档版本;不能只凭已有知识或内置文档声称已读。只有在线获取失败时才可使用内置离线副本,并报告失败原因、离线来源和版本。离线副本是打包快照,不保证最新,不得默默替代在线规范。最新规范与当前运行版本冲突时,说明能力差异和受阻步骤,不得用旧规范覆盖在线规范。

面向用户的沟通:需求引导、布局建议、确认选项以及安装和完成状态都使用业务语言,例如“整页展示业务内容,需要时再打开 AI 对话”或“边对话边查看辅助资料”。不要要求普通用户确认 customFrame、register()、repository、dshHomePath、reconcileLocalProviders 等代码字段、内部身份或占位仓库地址,也不要大段呈现内部实现后让用户选择。技术字段由 Agent 在开发执行时按本规范处理;缺少发布身份不能阻断已明确的本地开发,不得捏造真实仓库。确实需要用户提供信息时,只询问影响业务或安装的实际问题,例如“要保存在哪个文件夹?”;用户主动询问技术细节时再简短解释。完成汇报先说明能做什么、哪些操作已实测和哪些仍待验证,修改文件与技术检查作为简短附录,最后按下方要求提示用户如何开始使用。

每次完成工作台开发后,最终回复必须以醒目的「接下来怎么用」三点提示收尾,放在所有修改说明、自测结果、文件链接和技术附录之后;不能只在开头提醒,也不能在这三点之后追加内容:

  1. 开发结果:用工作台实际名称说明已完成的开发和安装状态。只有确实完成开发并安装后才能说“已开发好并安装到本机”;未完成或安装失败时如实说明卡在哪里,不得声称已加载或全部验收通过。
  2. 如何重启:安装成功且需要重启时,请用户等当前任务结束后,点击顶部提示中的「立即重启」,或使用菜单「Harness → 重启 Harness」;通常只需重启 Harness,无需退出 DSH Desktop。只有重启 Harness 后仍未正常加载且排查确认需要时,才建议完整退出并重新打开 DSH Desktop。不得自行关闭或重启用户正在使用的应用;无需重启或安装未完成时如实说明,不把重启当作安装失败的解决办法。
  3. 去哪里打开:重启后,展开左侧栏,在左上角「工作台」右侧的当前名称/切换区域,选择刚制作的工作台(写出实际名称)。若没看到,进入「工作台市场 → 已安装」查找带「本地」标识的条目并检查运行状态,加载或启用成功后再切换。用户可以直接试用;如需继续验收,也可以回到原对话说「重启好了」,不必返回对话才能使用。安装未完成时明确此入口仍待安装和加载后确认。

需求确认(先于写代码):用渐进对话了解本次要做的工作台,每次只问一个会影响下一步的必要问题。先复用当前请求及已确认的上下文,不重复询问已提供的信息。名称未知时,先问“这个工作台想叫什么名字?”;可以使用暂定名,名称不应成为开工的阻碍。随后按已有信息补问目标使用者、主要功能或业务场景,不使用固定问卷,也不把多个问题塞进一次提问。用户不清楚核心流程时,根据回答提出一个最小流程建议,请用户确认或调整,不要求用户独立填全需求表。业务目标和最小实现范围已足够明确并经用户确认后即可开始,不必补齐每个形式字段。通用“开发指令”、目录名、已有代码或依赖不能自行当作开发意图。在业务意图或实现范围仍不清楚时,等待当前问题的回答;可以只读检查目录、版本、脚本和宿主能力,不创建或修改业务代码、界面或包文件,也不构建、打包、安装或投稿。

需求明确后,从项目读取名称、作者、版本、说明和测试命令,只询问无法查明且会影响实现的必要信息。开发请求本身不代表授权公开代码、商业数据或凭证。

开发前必须检查:

一页执行流程

  1. 确认需求:复用已确认的信息,每次只补问一个必要问题;名称可以暂定,流程可以先建议再确认。业务目标及最小范围足够清楚后开始,不要求填全表。通用指令或目录本身不算确认。
  2. 检查项目:确定项目根目录,运行 pwd、git status --short(若是 Git 仓库),查看已有代码、包管理器、未提交改动和目标 Desktop 版本;保留无关改动。
  3. 确定最小业务流程与布局:写下入口、主要动作、完成状态及失败恢复;按第 4 节明确选择标准分栏或 customFrame、首次无会话时的入口,再决定所需宿主能力。
  4. 实现:按第 3 节建立插件包和注册;第 4–7 节按责任及实际使用的能力落实。
  5. 包校验:运行 pnpm pack,检查 tarball 内 package.json、声明的入口和 cordis.patch.yml,检查大小和敏感文件。
  6. 本机安装实测:按 3.6 探测当前 Desktop 的安装能力、安装该 tarball、完成所需重启,并按第 8 节记录真实 UI 与加载证据。在目标 DSH Desktop 内开发时,按下方“同机重启边界”交给用户手动重启。

模型完成构建和包校验后,直接调用 install_local_workbench({ path: "<本机 .tgz 绝对路径>", replace: false }),内部统一使用 Plugin Manager 的 installBundle,无需用户填写路径或操作安装页面。更新用户已授权修改的同名工作台时传 replace: true。读取 application:applied 时直接核验界面,只有 restart-required 才提示用户重启 Harness;失败、取消和 overridden 不算已生效。

同机重启边界:Agent 不要自行关闭或重启 DSH Desktop、Harness,也不要用 ps、pgrep 等进程探测管理重启;请用户手动重启。用户重启 Harness 后,给用户两条可选路径:① 直接试用:展开左侧栏,点击左上角「工作台」右侧的当前工作台名称/切换区域,选择新工作台;若未显示,进入工作台市场的「已安装」检查条目和运行状态,加载或启用成功后再切换。新本地工作台加载后会自动加入切换菜单,不存在单独的「添加」按钮。② 继续 Agent 验收:回到原开发对话告诉 Agent「重启好了」,由 Agent 按第 8 节继续检查加载、已安装列表、切换入口和真实业务界面。用户可自行试用,不必返回对话;Agent 尚未实测的项目仍标为待验证,安装成功不能表述为全部验收通过。运行在目标应用之外的 Agent 可按正常重启流程操作。

空目录演练:目录为空且用户只粘贴通用“开发指令”或说“做个工作台”时,先复用对话中已有的信息。名称未知就只问“这个工作台想叫什么名字?”,并允许暂定名;下一轮再按回答补问一个最影响开发的业务问题,例如“它主要想帮你完成什么事?”。若用户已经说明用途,则跳过这类提问,根据现有信息建议最小流程并确认。可以只读检查目录与 Desktop 能力;业务目标及范围足够明确前,不创建包文件、业务代码或页面,也不构建、安装或投稿。不要把名称答复当作完整的业务需求。

2. 工作台是什么

一个工作台包同时是:

同一个工作台有以下标识,职责不同,不能混用:

标识在哪里用途
npm 包名package.json 的 name安装、依赖解析、cordis.patch.yml 的 name:
插件条目 idcordis.patch.yml 中 insert 行的 idHarness 插件树中定位这一行
本地工作台身份宿主根据真实客户端 npm 包名自动生成local:<npm 包名>,例如 local:my-workbench-package 或 local:@scope/name;侧栏入口和会话归属共用
仓库工作台身份有效的 GitHub repository URL、市场条目 id 及既有归属记录小写 owner/repository;已有身份保持兼容,市场卡片、侧栏入口和会话归属共用
兼容用旧 ID老包 register({ id })、旧目录 workbenchId仅迁移旧数据时使用;新包和新投稿都不填写

本地开发、安装和使用不需要 GitHub 账号、仓库 URL、创建仓库或上传代码,不得填写虚构或占位仓库。宿主使用真实客户端包来源,作者不要手工填写本地 ID。升级版本或移动项目目录不会改变本地身份;修改 npm 包名会生成新身份。日后上架不会自动迁移本地会话,也不会静默覆盖本地身份;必须明确设计并确认迁移,本规范不提供自动本地到市场迁移。

Desktop 将 GitHub owner 和 repo 规范化为小写,例如 https://github.com/Owner/Repo 对应 owner/repo。仓库改名或转移会改变工作台身份,需按迁移规则处理原有会话,不能仅改 URL。旧版 wb-owner-repo 只作为兼容映射,不是新包的注册 ID。

3. 包格式

3.1 目录结构

一个包只放一个工作台。典型结构:

package.json          # npm 与 DSH 插件声明
cordis.patch.yml      # 把服务端入口插入插件树
dsh/index.js          # 服务端入口(exports["."])
lib/client.js         # 已构建的客户端入口(exports["./client"])
README.md  LICENSE

3.2 package.json

字段级别要求
name、version必须普通 npm 字段,version 为完整 SemVer(如 1.2.0)
"type": "module"建议所有官方示例都使用 ES 模块
main 与 exports["."]必须指向服务端入口,cordis.patch.yml 导入的就是它
dsh.bundle.patch必须例如 "dsh": { "bundle": { "patch": "./cordis.patch.yml" } },路径相对包根目录。没有它,包只会被当作普通依赖安装,不会激活任何插件层
dsh.client.platform必须固定为 "web",否则客户端模块不会被加载
dsh.client.inject必须字符串数组,必须包含 dsh-desktop-workbenches,保证工作台服务先于你的客户端加载;用到其他客户端包时一并列出。这里只决定加载顺序,不是 Cordis 服务注入
exports["./client"]必须声明了 dsh.client 就必须导出已构建的客户端文件,否则启动时报错
dsh.client.external可选客户端需要 require 共享基线以外的模块(包括子路径)时列出;不能列自己
exports["./package.json"]、exports["./cordis.patch.yml"]建议与官方包保持一致
files建议必须把 cordis.patch.yml、服务端入口和构建产物包含进去
peerDependencies建议官方 @deepseek-ai/* 包(如 @deepseek-ai/cordis、@deepseek-ai/dsh-client-connection)写在 peerDependencies,不要写在 dependencies,避免装进第二份宿主代码。版本范围要显式包含预发布分支,例如 ">=0.1.5-rc.2 <0.2.0-0",否则预发布版的 Harness 会被排除,安装时报 ERESOLVE
@deepseek-ai/schemastery建议服务端 Config 使用它定义;它是运行时依赖
license、author、description建议真实填写

不要依赖未文档化的 dsh 字段(例如 dsh.client.inline),它们会被静默忽略。

3.3 cordis.patch.yml

文件是一个 YAML 数组。工作台通常只需要一条 insert:

- insert:
    - id: my-workbench            # 插件条目 id
      name: my-workbench-package  # 必须写 npm 包名,Node 才能解析到安装的代码
      config:
        root: !!js dshHomePath('my-workbench')   # 可选:业务数据目录

3.4 服务端入口

import Schema from '@deepseek-ai/schemastery'

export const name = 'my-workbench'
export const inject = ['connection']
export const Config = Schema.object({ root: Schema.string().required() })

export function apply(ctx, config) {
  // 注册本工作台自己的本机接口,例如 /api/my-workbench/...
}

3.5 客户端入口

客户端文件必须是已构建好的单文件模块,通过模块加载器注册:

window.__ModuleLoader__.load({
  id: 'my-workbench-package',
  factory: (require) => {
    const React = require('react')
    function BusinessPanel({ service, entry }) { /* 业务面板 */ }
    function apply(ctx) {
      ctx.effect(() => ctx.desktopWorkbenches.register({
        title: '我的工作台',
        description: '说明它解决什么问题'
      }, BusinessPanel))
    }
    return { apply, inject: ['desktopWorkbenches'] }
  }
})

3.6 构建与本地安装

4. 界面边界

工作台是一种面向具体工作场景的插件。本规范约束工作台、会话、工作区的关联关系和切换行为,不规定统一的业务面板设计。

工作台作者或用户可以定义业务面板的布局、内容、工具栏和业务操作。接入已有工作台时,优先复用原有界面和业务绑定流程,不要求加入统一顶部工具栏、会话下拉框、业务区折叠按钮或“通用聊天”按钮。

4.1 先按主任务选择布局

向用户建议时说:“建议先整页展示业务内容,需要时再打开 AI 对话;如果你的主要工作是和 AI 讨论,也可以边对话边查看辅助资料。”只确认这两种体验的取舍;下方 API 与布局参数由 Agent 实现,不作为用户确认项。

不要把常驻会话区当作所有工作台的默认产品设计。先向用户确认主任务;需求不明确时,在业务整页与对话分栏之间给出建议和取舍,再选择布局。技术上未声明 customFrame 仍使用标准分栏,但开发时应主动选择,而不是沿用这个默认值。

无论选择哪种布局,都要按业务内容容器的实际宽度设计响应式布局;整窗媒体查询不能代表侧栏展开、分栏及嵌入后的可用宽度。可用容器查询或测量业务根容器,在空间不足时让多列内容重新排布、折叠次要信息或允许有界滚动;长中文标题、英文标识、数字和操作按钮均应保持可读可用,避免单字竖排、按钮裁切和整页横向溢出。

市场、工作台切换和设置等公共入口由 DSH 保留,业务面板不得遮挡。使用 customFrame(见 3.5)的工作台必须限制在宿主分配的主内容区域内:根容器应按正常 flex 布局填满该区域;内部使用绝对定位、窗口最大化或拖拽换位时,都不得越过宿主容器的定位与裁剪边界,也不得覆盖 Desktop 左侧会话栏。原生会话由宿主提供的 conversation 节点放进业务布局,不能再创建一套聊天界面。原生会话能力与模式切换沿用 Desktop 的规则。

顶部布局:默认与紧凑

register() 的 headerLayout 可选值为 'default'(省略时默认)和 'compact';其他值注册失败。侧栏展开时保持工作台原布局,不新增空白行;宿主将既有顶部区域(--dsh-frame-top-clearance,通常 48px)中的非交互部分设置为拖动区域,并按实际控件矩形排除按钮、输入、菜单、iframe 与自定义 data-dsh-no-drag 区域。默认模式仅在 macOS 非全屏且侧栏收起时预留独立系统控件行,业务内容从该行下方开始,不依赖内部 <header> 的层级。Windows 使用独立标题栏;全屏时不额外占用此行。旧工作台不声明时采用默认模式。宿主不推断第一个 <header> 是工具栏,也不清除工作台自身的标题内边距。

紧凑模式可用于任何 customFrame: true 工作台,或 layout.businessSide: 'left' 的标准分栏工作台(无需设置 embedded):

ctx.desktopWorkbenches.register({
  title: '业务工具',
  customFrame: true, headerLayout: 'compact'
}, BusinessPanel)
// BusinessPanel 渲染的同一文档中,唯一一行工具栏:
React.createElement('header', { 'data-dsh-workbench-toolbar': '' },
  React.createElement('span', null, '业务工具'),
  React.createElement('button', { onClick: refresh }, '刷新'))

普通左侧分栏仍保留侧边与底部的业务间距;仅有效紧凑模式取消顶部间距,工具栏以 sticky 保持在顶部,正文可滚动。工具栏祖先必须覆盖正文高度,不得另加顶部 margin/padding 或独立滚动容器。

标记工具栏必须位于面板顶部、占满宿主可用宽度,可嵌套在容器中。普通左侧分栏的右侧会话使用既有顶部空白作为拖动区域,不新增独立行;customFrame 的工具栏必须跨整个自定义框架宽度,不能仅覆盖业务子栏,右侧空白的拖动由这条全宽工具栏提供。宿主仅为这一行设置最低高度和左侧控件避让,空白可拖动;按钮、链接、表单控件及标准交互 role 排除拖动。自定义交互区、展开菜单和弹层加 data-dsh-no-drag。宿主内部标记 data-dsh-compact-toolbar 和 data-dsh-compact-valid 不得由工作台设置。不要自行重复系统控件 padding 或 drag/no-drag 整体覆盖规则。

标记缺失或不唯一、右侧分栏不满足共享位置条件时,安全回退默认系统控件行。iframe 内部 DOM 不属于此协议;包含 iframe 的工作台可将唯一工具栏放在同一文档的外部容器,不能标记整个 iframe 来充当可拖动顶栏。工具栏按业务容器宽度重排;长标题缩略、次要描述放在下一行或正文,按钮不得压成不可用尺寸。

验收两种模式:侧栏展开/收起、窄容器、中英文长标题、全屏进入/退出,确认无双重留白或控件遮挡;点击按钮、编辑输入、打开菜单和键盘焦点正常;实际拖动空白移动窗口。DOM/CSS 校验不能替代原生窗口拖动验收。

旧工作台迁移与兼容边界

这是显式选择协议:宿主不能同时保证任意旧 DOM 不被系统控件遮挡,并自动保留所有旧紧凑布局。未迁移工作台仍可加载,采用默认安全布局;仅 macOS 非全屏且侧栏收起时默认布局增加独立控件行;侧栏展开保持原布局。

当前工作台迁移操作迁移前行为
标题与介绍等默认业务布局可保持不声明;核对并移除工作台自行添加的系统控件避让,保留正常业务 padding展开保留原布局;收起使用独立系统控件行
希望共享顶栏的 customFrame 或左侧分栏在注册信息声明 headerLayout: 'compact',在同一文档标记唯一的 data-dsh-workbench-toolbar,移除重复的控件避让展开保留原布局;收起使用独立系统控件行,不猜测工具栏
工具栏在 iframe 中、右侧分栏或多个标记将单一工具栏移到支持的同一文档位置,或保持默认布局安全回退默认布局

迁移只通过公开注册字段和工具栏标记,不使用工作台名称白名单或语义 header 自动识别。外部工作台的维护者须自行发布迁移版本;宿主升级不修改已安装的外部包。普通左侧分栏仅在有效紧凑声明下取消宿主的顶部 18px 间距,默认模式和工作台自身的 header padding 保持原值。

5. 工作台、会话与工作区

6. 未绑定会话时的使用

打开工作台后,即使没有会话或工作区,也应立即显示业务面板。浏览、创建、选择业务档案或项目,不得以已有原生会话或工作区为前提。

仅当用户执行向 Agent 发起请求、写入会话草稿等依赖会话的操作时,才检查当前会话是否属于该工作台。尚未绑定时,在该操作处引导创建或打开会话,不阻断其他业务功能。

工作台内部应提供“在此工作区新建工作台会话”或“选择资料位置并开始工作台会话”等明确动作。业务项目本身不需要文件系统时可以先创建业务记录;一旦需要保存文件或创建 DSH 工作区,必须先调用宿主目录选择器,由用户选择已有目录或在系统选择器中明确新建目录。工作台不得在默认位置、用户主目录、当前仓库或其他推测路径中静默创建文件夹。确认位置后再创建工作区和会话、登记归属、保存业务关联并填入开场草稿;取消时不得留下目录、空项目、工作区、会话或绑定。已有业务资料只能恢复用户此前确认的位置;位置不存在时重新选择,不得自动创建同名目录。失败时保留可重试状态,不得默默改用无关工作区。

先选择业务档案或项目,再创建第一条会话时,应保留当前业务选择。打开已有会话时,优先恢复该会话已有的业务映射,不被其他会话最近选择覆盖。

接入已有工作台时保留原有的项目创建引导和独立业务流程。用户无需先手工新建会话;工作台通过 Desktop 接口发起创建或恢复,由宿主校验并登记归属。开场草稿保留用户已输入的内容并由用户发送;创建失败或延迟时保留草稿供重试。自动执行的业务流程必须在正确归属的会话中运行,不能与开场草稿混用,也不能由隐藏工作台触发误发送。

7. 模式入口与切换

宿主在侧栏顶部提供单一模式切换器:按钮只显示当前模式,展开后可在“原生会话”和已添加工作台之间切换;“工作台管理”入口常驻最右侧。工作台不得复制、遮挡或替换这些公共入口。切换模式只改变前台展示和会话筛选,不执行卸载重装,也不停止后台任务。

“原生会话”和各工作台是平行模式,当前模式决定可见会话和新建归属:

显式打开未绑定会话时切换到“原生会话”;显式打开已绑定且 owner 可用的会话时切换到对应工作台。owner 已移除、卸载或不可用时按普通会话处理,不自动唤起或重装。异步创建期间如果用户继续导航、切换模式或移除 owner,不得把界面拉回旧模式、覆盖后续选择或绑定到失效 owner。

切换模式不得自动收起或展开左侧会话栏;侧栏状态由用户控制并在模式间保持。工作台会话标题前显示所属工作台的小图标,普通会话不显示;图标只表示归属,并提供可访问名称或提示。折叠态与展开态中的按钮、菜单、选中态和焦点轮廓都不得裁切、重叠或溢出。

首次进入工作台可以显示首页、空会话状态或按工作台约定初始化会话,但业务面板不能依赖会话初始化完成。后续进入恢复该工作台最近会话。导航和切换不得删除会话、业务资料、项目文件、收藏或笔记。

8. 本地自测清单

验证省略 repository 的本地包(包括 scoped npm 包名):安装加载后出现带「本地」标识的条目和切换入口,业务面板可打开;重载或版本更新后身份及会话归属稳定。另验证注册失败时工作台管理显示包名、阶段和原因,修复重载后诊断消失。模块 HTTP 200、导入成功和测试通过均不能替代真实 UI 验收。

开发完成后按责任逐项确认。“宿主负责”是对当前 Desktop 能力的观察和问题反馈,插件作者不得为此复制宿主界面;标为“仅使用该能力时适用”的项,不使用时记“不适用”及原因。全部适用项有证据通过,才能称为本机自测通过。

包与加载

第 4 节:界面边界

第 5–6 节:工作区与会话

第 7 节:模式切换与保留

9. 失败时如何交付

最终必须列出真实结果和证据:修改的文件、包版本、本机安装与自测结果、未验证的项目。缺哪一项就写哪一项,不虚构成功。缺少工具链、安装失败或宿主接口不可用时,保留已完成的代码,说明原因和下一步。

参考