# dsh-task-board —— DeepSeek Harness 任务看板插件 [English](README.md) · [中文](README.zh.md) 在 DSH Desktop Web GUI 的顶部页签条(与「对话」「轨迹」并列)新增一个 **「任务」** 页签, 提供可视化的任务管理看板: - **三栏看板**:创建任务(待办)/ 执行任务(进行中)/ 完成任务(完成) - **任务组**:每栏内按「任务组」字段分组,点击组名可折叠/展开该组全部卡片 - **任务卡片**:id(UUID)、标题、任务组、描述(Markdown)、工作目录、关联文件、状态 - **新建 / 编辑 / 删除**:右上角「新建任务」弹窗录入全部字段;卡片上提供编辑与删除 - **拖拽执行**:把卡片拖进「执行任务」栏 → 插件自动为它创建一个绑定 `workDir` 的 独立 DSH 会话,把卡片描述作为 Prompt 以 queue 模式发送给 Agent 真实执行; 执行中卡片实时显示输出;出现回合结束且会话空闲后自动流转到「完成任务」栏 - **重跑**:把「完成任务」栏里的卡片再次拖进「执行任务」栏即可重新执行 - **失败处理**:prompt 被拒 / Agent 报错 / 超时(8 小时)→ 回到「创建任务」栏并在卡上 显示错误信息,可再次拖入执行 - **本地持久化**:任务数据存于浏览器 localStorage(key `dsh-task-board:v1`),刷新不丢 - **与主题一致**:颜色全部使用 DSH 主题 CSS 变量(`--dsw-*`),自动适配浅色/暗色 代码注释为中文。 --- ## 目录结构 ``` dsh-task-board/ ├─ package.json # 包清单:dsh.client(web)+ dsh.bundle(patch)双声明 ├─ cordis.patch.yml # bundle patch:把本插件作为一行插入 Loader 组合树 ├─ tsconfig.json # 仅供编辑器/类型阅读;构建不依赖它 ├─ lib/ │ ├─ index.js # host 半边(Loader 在 Node 侧装载的空插件) │ └─ client.js # 浏览器半边(构建产物,禁止手改;改 src/client.tsx 后重跑 build) ├─ src/ │ └─ client.tsx # 浏览器半边源码(全部看板逻辑,中文注释) └─ scripts/ └─ build.mjs # 构建脚本:TSX → CJS + ModuleLoader 注册壳 → lib/client.js ``` ### 双面体说明 本插件是一个标准的 DSH「客户端插件」npm 包,同一包包含两个半边: | 半边 | 文件 | 作用 | |---|---|---| | host 半边 | `lib/index.js` | Loader 在 Node 侧装载的 Cordis 插件;纯 UI 插件为空 `apply` | | 浏览器半边 | `lib/client.js` | 注册进 Web GUI 的客户端插件:`{ inject, apply }` | `package.json` 的两处关键声明(机制证据见 DSH 官方 `@deepseek-ai/dsh-client-*` 包): ```jsonc "dsh": { "client": { // 浏览器半边声明(platform 必须是 "web") "platform": "web", "inject": [ /* 图中依赖的其它客户端包 */ ] }, "bundle": { // 作为 profile bundle 的声明(提供 Loader patch) "patch": "./cordis.patch.yml" } } ``` 浏览器半边必须整体是一个注册包: ```js window.__ModuleLoader__.load({ id: "dsh-task-board", factory: (require) => { var module = { exports: {} }; var exports = module.exports; // ...本插件代码... return module.exports; // { inject: [...], apply(ctx) } } }); ``` `apply(ctx)` 里通过 `ctx.slots.inject("conversation.view", …)` 注册页签, 这是「对话」/「轨迹」页签的同一官方机制(`conversation.view` 是 list 槽)。 --- ## 环境与构建 - 需要 Node.js(本机验证于 v22.19)。 - **离线可用**:构建脚本使用 DSH profile 里自带的 TypeScript(`~/.dsh/profiles/node_modules/typescript`) 做纯转译,不联网、不安装任何依赖。也可用 `DSH_TS_LIB` 环境变量指定 typescript.js 路径。 ```powershell # 重新生成 lib/client.js(改过 src/client.tsx 后执行) node scripts/build.mjs # 可选:监听 src/client.tsx 变化自动重建(配合客户端 HMR 调试) node scripts/build.mjs --watch ``` > 注:`lib/client.js` 是提交产物;不改源码就不需要跑构建。 --- ## 安装到 DSH Desktop(激活 profile) 本插件的激活目标是桌面 profile(当前 GUI 即由它驱动)。三选一: ### 方式 A:DSH 终端 CLI(推荐) 在 DSH Desktop 内启动的 DSH 终端中执行: ```sh dsh plugin --profile desktop add <本目录绝对路径> ``` 说明: - 本包声明了 `dsh.bundle`,因此 CLI 会自动把它加入 `~/.dsh/profiles/desktop/package.json` 的 `dsh.profile.bundles`, 并作为直接依赖安装进 profile。 - 因为插件 manifest(bundles 清单)发生变化,**必须重启 DSH Desktop** 才能生效。 重启后顶部页签条会出现「任务」(需先打开/新建一个会话——页签条属于会话头部, 与「对话」「轨迹」同机制)。 ### 方式 B:手工安装(不依赖 CLI) 1. 编辑 `~/.dsh/profiles/desktop/package.json`: - `dependencies` 增加本包(file: 指向绝对路径); - `dsh.profile.bundles` 增加 `"@linsibin/dsh-task-board"`。 2. 在 profile 目录执行 `pnpm install`(若本机 pnpm 不可用可改用 `dsh plugin --profile desktop install`)。 3. 重启 DSH Desktop。 ### 方式 C:npm 安装 ```sh npm install @linsibin/dsh-task-board ``` 然后在 desktop profile 的 `dsh.profile.bundles` 中加入 `"@linsibin/dsh-task-board"` 并重启 DSH Desktop。 ### 方式 C:内置插件市场(如可用) 若桌面设置 → Plugins 中能看到市场,可按市场安装流程添加本地目录或已发布包; 未发布时优先用方式 A / B。 > 不要手工编辑 `cordis.yml`——它在每次启动时会被重写。用户层改动请走 > `cordis.patch.yml` 或上述安装流程。 --- ## 验证清单(验收标准对照) | 验收项 | 如何验证 | |---|---| | 顶部出现「任务」页签 | 打开任意会话 → 会话头部页签条出现「任务」(在「对话」「轨迹」之后) | | 新建包含任务组与文件的卡片 | 点「新建任务」→ 填标题/组/描述/工作目录/文件(每行一个)→ 保存 → 卡片出现在「创建任务」栏并按组显示 | | 拖拽触发执行 / AI 开始回复 | 把卡片拖到「执行任务」栏 → 卡片出现运行指示、实时输出;浏览器控制台打印 `[dsh-task-board]` 日志;侧栏会话列表出现由插件创建的新会话 | | 完成后自动流转 | Agent 回合结束且会话空闲 → 卡片自动进入「完成任务」栏并带结果摘要 | | 完成后可拖回重跑 | 把「完成任务」栏卡片再拖进「执行任务」栏 → 重新执行 | | 任务组折叠/展开 | 点击组名折叠或展开该组全部卡片 | | 状态持久化 | 刷新页面后任务仍存在 | **失败路径**:prompt 被拒、Agent 报错或超时会回到「创建任务」栏并显示 `⚠` 错误, 可再次拖入重试。 --- ## 实现原理(执行链路) 卡片拖入「执行任务」栏后,浏览器半边执行: 1. `ctx.sessions.create({ cwd: workDir })` —— 为该卡创建**独立会话**(host 接受任意 绝对 cwd,不经 workspace 注册表)。空 workDir 则不传,使用默认工作目录。 2. `ctx.sessions.binding(sessionId)` → `binding.session.open()` —— 拿到会话对象并打开事件窗口。 3. `session.prompt([{ type: 'text', text: 任务描述 }], 'queue')` —— 发送任务。 4. 订阅 `binding.eventSource`:实时收集 `assistant/message`、`assistant/chunk` 文本回填卡片。 5. 轮询会话快照:出现 `turn/end` 且 `running === false` 且队列清空 → 判定完成, 自动置为 `completed`;`promptError` / `lastAgentError` → 失败回待办;超过 8 小时强制回待办。 6. 停止:调用 `session.cancel()`(降级 `remote.session.cancel`)并回待办。 > 每次执行都会新建一个会话并保留在 GUI 会话列表里(卡片记录 `sessionId`), > 便于事后打开该会话复查执行全过程。 --- ## 常见坑与备注 - **页签只在有会话时出现**:`conversation.view` 槽挂在会话头部,必须先打开/新建一个 会话才能看到页签条(「对话/轨迹/任务」)。这是官方机制,非本插件限制。 - **重启边界**:首次加入 bundle 后必须重启 DSH Desktop;之后仅改 `src/client.tsx` 并 重新构建 `lib/client.js`,可利用 DSH 常驻的客户端 HMR 原地热替换该插件(无需整页刷新, 但插件内 React 状态会丢失;不改 manifest 就不必重启)。 - **不要修改** `cordis.yml`、安装目录(Program Files 下 asar 内容)。 - **任意目录执行**:`session.create` 接受任意绝对 cwd,但主机 fs/sandbox 策略是否放行 未登记目录取决于主机配置;若被拦,请先通过 GUI 把该目录登记为工作区,或改填已有工作区。 - **浏览器半边纯度门**:`lib/client.js` 运行时只能 `require` 平台 seed 与图内包, 因此本插件未引入任何第三方运行时依赖;源码注释中的类型仅为阅读用。 --- ## 开发备忘 - 改源码:编辑 `src/client.tsx` → `node scripts/build.mjs` → 观察 HMR 或刷新页面。 - 改页签文案:编辑文件末尾 `zh` / `en` 字典。 - 加字段:`Task` 接口 + 新建/编辑弹窗 + 卡片渲染三处同步修改。 - 构建脚本定位 TypeScript 失败时:设置环境变量 `DSH_TS_LIB` 指向 `typescript.js`。