# @cnbmtech/dsh-zentao-session 中文文档

规范地址：https://npmcn.com/package/%40cnbmtech/dsh-zentao-session/

版本：0\.6\.9
npm 元数据更新时间：2026-11-04T17:16:27.272Z
中文译文更新时间：2026-11-10T04:30:52.600Z
软件包许可：MIT

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40cnbmtech/dsh-zentao-session
原文对照：https://npmcn.com/package/%40cnbmtech/dsh-zentao-session/?lang=original

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：430 kB，10 个文件
- 模块格式：ES Module
- TypeScript 类型：未声明
- Node.js 要求：^22\.19\.0 \|\| \>=24\.0\.0
- 运行依赖：0 个
- peer 依赖：react ^18\.2\.0，@deepseek-ai/cordis \>=4\.0\.0，@deepseek-ai/dsh-tools \>=0\.1\.0-rc\.2 \<0\.3\.0，@deepseek-ai/schemastery \>=3\.18\.0
- 首次发布：2026-10-15T06:32:49.525Z
- 最新版本发布：2026-11-04T17:16:27.065Z
- 近 30 天下载量：2346（2026-10-10 至 2026-11-08，npm 官方下载统计）
- 已发布版本数：22（近 12 个月 22 个）

## 中文简介

ZenTao (QQ) 基于会话的 DeepSeek Harness 工具和面板: 我的bug, 任务和故事横跨浏览范围, bug 确认/解析, 评论文章和评论编辑, 加上拖到聊天的 Markdown 引用. 与缺少 REST API 的遗留 ZenTao 版本一起工作

## 安装

```sh
npm install @cnbmtech/dsh-zentao-session
```

也可以使用：`pnpm add @cnbmtech/dsh-zentao-session`、`yarn add @cnbmtech/dsh-zentao-session`、`bun add @cnbmtech/dsh-zentao-session`

## README

# @cnbmtech/dsh-zentao-session

[DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 的**禅道（ZenTao）会话式集成插件**：11 个模型工具 + 一个「我的工作」面板（BUG / 任务 / 需求），可把条目一键送进对话。

> 走禅道的**会话式接口**（`/my-*.json` + 表单页），不依赖 REST API v1/v2 —— 因此在那些 `api.php` 被网关改造、官方 REST 不可用的禅道实例上照样能用。

## 能力

**面板**（左侧栏「更多」菜单 → 「禅道」；同一行右边的「侧边栏 + 眼睛」可让它常驻左侧栏，见「侧栏入口」）

-   三个页签：我的 BUG / 我的任务 / 我的需求，每个页签自带**浏览范围**下拉（指派给我 / 由我创建 / 由我解决|完成 / 我评审 / 由我关闭 …，与禅道「我的地盘」的 tab 逐一对应）
    
-   **列表头只留刷新，条数与分页在卡片页脚（整组靠右）**：页脚从右往左是分页（`‹ 当前页 / 总页数 ›` + 每页 20 / 50 / 100）与条数（`共 N 条`）， 分页只此一处（早期条数写成「7 / 7 条」与页码「1 / 1」并排，会被读成两处分页；后来挤进头部，面板一窄就整组掉行、看着像错位，故下沉到页脚并整体贴右）；刷新按当前范围 + 当前页重新取数，不会把第 2 页打回第 1 页； 筛选/搜索激活时刷新改走 `query` 通道**强制重拉**（`force=1`，连行集/计数缓存一起绕过），既保持筛选口径也拿到新数据（2026-10-30 修复：此前刷新会把未过滤口径覆盖回筛选视图）
    
-   **条数口径统一为服务端总数**：无筛选时，页脚条数、页签上的数字、统计卡主卡三处都用服务端返回的总数（recTotal），不再各说各话； 统计卡的**子卡（未确认 / 已确认 / 已解决 …）是全范围计数**：禅道的「我的」接口不给分状态总数（`pager` 里只有 recTotal / pageTotal，且任何查询参数都被忽略）， 所以由宿主侧把当前范围整批取回后本地按状态 / 确认度聚合（每页 200 条并发拉，最多 40 页），结果带 60 秒缓存，浏览器端只收几十字节； 子卡文案写明「全范围 …」，聚合不全时（超出页数上限或部分页失败）自动退回本页口径并标「本页 …」
    
-   **列表按所属产品 / 所属项目分组（2026-10-30）**：列表默认分组展示，组头是一行吸顶的「所属产品：<名称> (N)」（任务页签是「所属项目：<名称> (N)」）， 组头本身是纯标签：置灰、不可点，不额外缩进（只留一个空格不贴边），只用来标出下面这组条目属于谁； 同组条目按列表原顺序排在一起；BUG 与需求按**产品**（对齐禅道 BUG/需求列表的「所属产品」维度），任务按**项目**（对齐任务列表的「所属项目」列）， 无归属或取不到名称的条目归到末尾「未关联产品 / 未关联项目」。BUG 行 JSON 只有 product / project 两个数字 id（实测 56 个字段里没有名称）， 宿主借 `/product-all.json`（实测 30 个产品）、`/project-all.json`（约 311 个项目）的 id→名称表补 `productTitle` / `projectName` （10 分钟缓存；任务行自带项目名时不发这次请求），筛选/搜索通道同样补，所以筛选后组头不会掉成「未关联」； 补名逻辑在**宿主侧**（Node），改了要重启宿主才生效；若宿主还是旧版（行里只有 product / project 数字 id）， 面板会自动拉一次 `targets` 列表（与新建弹窗同一份）把 id 译成名称再分组，不会整页落到「未关联」； 列表头的「按产品分组 / 按项目分组」按钮可切回平铺列表，偏好记在 `localStorage`（`dsh-zentao-group`）。 **组头括号里数是当前页该组的条数**——禅道一页 20 条，同一产品跨页时每页各出一组，想一屏看全某个产品就把每页条数调到 50 / 100 或直接用筛选
    
-   **列表行右侧显示处理时长（2026-10-30）**：每行右上角一个红色「N 天」+ 其下一条细胶囊进度条， 一眼看出这条挂了多久、相对承诺天数消耗到哪了。口径三条： **① 起点**取创建时间（`openedDate`），**终点**取解决 / 完成时间（BUG 用 `resolvedDate`、任务用 `finishedDate`）， 没走解决就直接关闭的条目才回落到 `closedDate`，都为空表示仍在处理、算到今天——所以「关闭」不会把处理时间虚增成验收时间； **② 只比日期不比时刻**（0 点对齐），与禅道网页的「N 天」一致，不会把 7 天 20 小时显示成 8 天； **③ 承诺天数**优先取该条目的 `deadline`（创建 → 截止），没人填截止日期时按优先级兜底 P1=3 / P2=7 / P3=15 / P4=30 天 （实测禅道行的 `deadline` 大量是 `0000-00-00`，只认它会让进度条永远按 0 算）。 进度条宽度 = 已用 / 承诺，超期时铺满并加深字重；悬停给出「创建 … · 仍在处理/解决 … · 已用 N 天 / 承诺 M 天（来源）」，超期还写明超了几天。 没有创建时间的行**不显示**这个标识（宁可不显示也不编一个天数）。任务行原本不带创建/完成时间，宿主侧已一并补齐。 布局：左栏（标题 + 元数据）可收缩、右栏固定 78px 靠右；宿主侧栏只给到 ~300px 时按**行自身宽度**换行到标题下方（不用视口宽度判断，侧栏变窄时视口往往仍然很宽）。 七套皮肤各有一条 `--dur` 令牌（深色皮肤把红提亮、波普压深一档），轨道统一用半透明色——固定浅灰在选中行的浅蓝底上会整个隐形
    
-   **筛选/搜索是全量的，不是本页的（2026-10-29）**：点统计卡筛选、点工具栏状态签、输搜索词（防抖 300ms），当前页签列表走宿主 `query` 通道—— 宿主复用 counts 聚合时的**整范围行集**（同一份缓存，零额外请求）按状态/确认度/关键词过滤后本地分页， 页脚与分页器切成**筛选集自己的口径**（如「已解决」= 共 81 条、1 / 5 页），与统计卡子卡数字天然一致，取消筛选即回到服务端总数； 行集被 40 页上限截断时，状态筛选走**排序聚类走查**兜底（关闭条目的 assignedTo='closed'、未关闭是真账号，按 assignedTo/status 排序目标簇聚在两端， 每方向最多走 40 页、遇簇边界即停）——此法不适用 confirmed 维度与搜索（无对应排序列），截断场景下它们仍按「本页」口径降级
    
-   **切换浏览范围，列表与数字一起刷新（2026-10-29 修复）**：筛选/搜索激活时，列表走 `query` 通道、统计卡走 `counts` 通道， 两条通道各自按「kind:范围」取数。此前 `query` 的 effect 依赖里漏了 `scopes`，于是**选中卡片后再切范围下拉**会出现 「统计卡数字变了、列表还是旧范围的行」。现在依赖补齐；同时主卡数字改为优先读 `counts.total`（与子卡同源）， 不再依赖 `loadLists` 的 `state.*Total`——筛选态下 `loadLists` 会跳过当前页签，那个字段本来就不会更新
    
-   **搜索框可一键清空（2026-10-29）**：有搜索词时右侧出现 `×`，点击或按 `Esc` 清空并回到第 1 页；清空只清搜索词、不动状态筛选。
    
-   **写操作后的联动刷新（2026-10-30）**：确认/解决/加备注后，当前页签的全量计数**强制重拉**（此前统计卡要等切页签/换范围才更新）； 翻页、改范围只重发**有变化的页签**，未变化的两类不再陪跑重拉；点主卡（本来就没有筛选时）是纯 no-op，不再把页码打回第 1 页； 退出登录会清掉统计卡计数与新建权限探测，换账号不残留上一账号的数字。 输入框外层由 `label` 改成 `div`——`label` 会把内部按钮的点击转发成输入框聚焦，清空按钮会被吞掉
    
-   **列表行显示真名（2026-10-29）**：禅道列表行 JSON 只有裸账号（实测 56 个字段无真名），宿主借 view 详情 JSON 内嵌的全量 users 表 （实测约 747 项，变化慢，缓存 10 分钟；详情接口顺手收割刷新）给行内账号附加 `*Name` 字段，列表指派列与详情相关人字段均显示真名； 关闭态占位账号 `closed` 按禅道网页写法显示「Closed」
    
-   **HTML 实体统一还原**：禅道按 `htmlspecialchars` 下发（`&` → `&amp;`、`'` → `&#039;`），标题/名称/产品名/模块路径/相关人真名/备注都解码， 且不限于 `&amp;`——命名实体（`&lt; &gt; &quot; &nbsp; &mdash; &hellip; &times; &copy; …`，共 80 余个）、十进制 `&#39;`、十六进制 `&#x1F600;` 都认， 只解一层（`&amp;amp;` 仍是 `&amp;`）。富文本字段（重现步骤/描述/备注 HTML）保持原样，解码交给渲染层
    
-   **新建**：工具栏「+ 新建BUG / 新建任务 / 新建需求」——弹出**禅道自己的创建表单**（字段、选项、默认值、联动条件都来自创建页）， 先选创建目标（BUG/需求 → 产品；任务 → 项目，下拉可搜索），提交后**回读新建对象的详情页**确认真的建成，并直接跳到它的详情
    
-   **没权限就不显示「新建」**：切页签时探一次该类型有没有创建权限（禅道没权限也返回 200，只是创建页里没有表单），没有就把按钮换成一行说明（含原因与探测地址），不再点进去撞红条
    
-   **新建表单按禅道复刻**：控件类型（下拉/多选/复选/**单选组**/日期/文件）、必填标记、帮助说明、隐藏域默认值都照创建页渲染； 同名单选项合并成一组（禅道「添加之后」是 3 个同名 radio —— 旧实现渲染成 3 个文本框，提交值还会后写覆盖前写）； 下拉与正文里的 HTML 实体（`&lt;p&gt;` 等）还原成原文；整行带 `class='hide'/'hidden'` 的按禅道显隐（有联动条件的交给条件，其余整行隐藏）； **核心内容栏是可粘图编辑器**——在里面按 Ctrl+V 粘贴截图会先上传到禅道（`/file-ajaxPasteImage-<uid>.html`，带本次表单令牌 kuid）， 再把返回的 `<img src="/file-read-N.png">` 插到光标处**就地预览**（经本机代理，不裸取外域）； 「附件」字段支持多选上传（单个 ≤ 8MB，base64 交给宿主后按 `multipart/form-data` 提交），超过 8MB 会在本地直接拦下； 选择器（下拉）展开是**浮层**：坐标按锚点实测、下方放不下就向上翻，不占行内位置，所以同一行的其它控件不会被挤走（点外部或 ESC 关闭）； **同一行的控件保持一行**（与创建页一致）：日期范围（日程规划 预计开始 ~ 截止日期）并排等宽、不拆成两行， 第 2..N 个控件（操作系统 / 浏览器 / 严重程度 / 优先级 / 预计工时）跟在主控件后面用行内小标签补名，不另起一行被缩进一个标签列； **标题/名称独占一行**（BUG标题、任务名称、需求名称铺满控件列，挤在同一行会把标题压窄、长标题看不全）， 它的附加控件（严重程度 / 优先级 / 预计工时）另起一行、与控件列左对齐； 单选组与左侧字段名在同一行、垂直居中
    
-   统计卡点击筛选、标题/编号搜索、列表与详情双栏、**详情全屏（面板内铺满）**（隐藏顶栏/页签/统计卡/搜索工具栏，详情卡贴边铺满面板，右上角刷新 + 退出；ESC 或再点退出）
    
-   工具栏右侧「新建 / 角色 / 状态筛选」是一个操作组：同 32px 基线、组内 8px 间距（原来 35/30/35px 高、9-13px 间距，三个控件看着互不相干）
    
-   详情里：富文本重现步骤/描述（图片走本机代理并可点击放大、左右切换）、基本信息、处理信息、关联对象、操作历史
    
-   BUG 可直接在面板里走禅道原生表单：确认 / 解决 / 关闭 / 激活 / 指派（提交前拉取真实表单字段，提交后回读证实）
    
-   添加备注（三段式模板：问题原因／解决方案／解决时间），**发错了可以直接改**：`zentao_bug_comment_edit` 按 actionId / 内容片段 / 最近一条定位后重写（实测禅道只给条目页**最新一条**备注渲染编辑表单，改旧备注会报错并告知最新那条的编号）
    
-   **「解决」模型也能直接做**：`zentao_bug_resolve` 走禅道自己的解决表单（解决方案 / 解决版本 / 解决日期 / 三段式备注），**默认解决方案=已解决（solve）、解决版本=主干（trunk）**，提交后回读 `status=resolved` 才算成功；只发备注的 `zentao_bug_comment` **不会**改状态
    
-   「在禅道中打开」链接附带会话参数，**免登录直达**
    
-   **拖拽 / 复制为 Markdown 引用**：把条目拖到聊天输入框，或点「复制」后粘贴——引用只有三行：**标题行**、**读全文行**（该调哪个工具、截图怎么拿）、**免登录原地址**
    
-   **按角色处理**：选好职位（开发/测试/产品/管理）→ 点「按【开发】处理」→ 在当前项目新建对话并自动发送「身份开场白 + 条目引用 + 角色任务书」；任务书排在**最后**（模型读到最后的才是要它做的事），且是**纯文本聊天口吻**——不用 Markdown 标题/加粗，只有条目引用那两行带「- 」引导符。开发那套是**四步处理流程**：查沉淀（只说「用这条记录的标题自己提炼业务词」，不写死任何示例）→ 给分析 → **我确认后再改码** → 回写；末步文案按条目类型替换（BUG 先给 `zentao_bug_resolve`：三段式写进 comment、默认 solution=solve（已解决）/ build=trunk（主干），提交后回读确认已解决；只补备注才用 `zentao_bug_comment`，并提醒「发错了不用补发更正，用 `zentao_bug_comment_edit` 改那条」；任务/需求没有回写通道，直接说明「文案交我，我在禅道里填」）
    
-   **提示词不回抄详情**：禅道详情页里能看到的东西（状态、优先级、正文、步骤、截图清单、信息缺口）一概不复制进对话——写进上下文只会又碎又容易失真；提示词只给 `【禅道BUG #201030】标题`、`- 读全文：zentao_bug_view({ id: '201030' })；截图按详情链接自行取回（带 zentaosid），取不到就明说，不要脑补；不要凭标题猜内容。`、`- 禅道原地址：https://…/bug-view-201030.html?zentaosid=…` 这几行
    
-   **截图靠模型自己取，取不到就明说**：条目里的图地址都带会话票据（`?zentaosid=…`，实测不带 cookie 也能取），「读全文」那行已写明截图要自行取回、取不到就明说别脑补；工具回包里的 `<img src>` 同样改写成绝对地址，模型直接复制就能取回来看
    
-   **七套皮肤随时切换**：顶栏右侧的下拉选 **原生**（跟随宿主明暗）/ **极光** / **波普** / **棱彩** / **青海波** / **记分牌** / **碳纤红**。 切换即时生效并记在 `localStorage` 的 `dsh-zentao-theme`，刷新后保持；七套皮肤共用同一套 DOM，只有配色与少量外观修饰不同， 所以分页唯一、页脚末位、浮层 portal 等结构行为在所有皮肤下完全一致。 各皮肤的浮层（选择器菜单、图片画廊）也跟随皮肤，不会掉回原生配色。
    
    | 皮肤 | 明暗 | 主色 | 记忆点 |
    | --- | --- | --- | --- |
    | 原生 | 跟随宿主 | 禅道蓝 | 与宿主浑然一体 |
    | 极光 | 暗 | 蓝紫渐变 | 流体光斑 + 玻璃卡 |
    | 波普 | 浅 | 红黄蓝三原色 | 波点底 + 粗描边 + 硬阴影 |
    | 棱彩 | 暗 | 紫/天蓝/青柠 | 马赛克彩窗虚化 + 磨砂卡 |
    | 青海波 | 浅 | 藍染靛 + 朱印红 | 青海波纹样 + 明朝体标题 |
    | 记分牌 | 暗 | 荧光绿 + 琥珀 | 数码管数字 + 等宽字 |
    | 碳纤红 | 暗 | 碳灰 + 赛车红 | 碳纤编织纹 + 切角按钮 |
    
    实现方式是「根节点挂 `zts-theme-<id>`，只换 CSS 令牌 + 少量外观修饰」，新增一套皮肤只需加一条 `SKINS` 项和一段令牌定义。 注意两条硬约束：**同一个颜色变量不能同时当「前景文字色」和「按钮底色」**（例：碳纤红的 `--blue` 是暗底上的红字、`--btn-primary` 是托白字的深红）； **浮层挂在 body 上，皮肤 class 必须一起带过去**，否则下拉菜单会掉回原生配色。
    
-   **面板动效可关（2026-11-02）**：顶栏的动效开关默认**开**，关掉只是根节点摘掉 `.zts-motion`，DOM 结构与布局完全不变； 开着时换皮肤走 260ms 颜色交叉渐变，极光 / 棱彩的氛围光跟同一个开关，偏好记在 `localStorage` 的 `dsh-zentao-motion`。 系统的「减少动态效果」不再单独压掉面板动画——默认开是选定的默认，想静止就关顶栏这个开关
    

**模型工具**

| 工具 | 用途 |
| --- | --- |
| `zentao_ping` | 连通性与凭据自检 |
| `zentao_my_bugs` / `zentao_my_tasks` / `zentao_my_stories` | 三类条目列表，`type` 指定浏览范围 |
| `zentao_bug_view` / `zentao_story_view` / `zentao_task_view` | 单条完整详情（正文、截图地址、相关人、操作历史） |
| `zentao_bug_confirm` | 确认 BUG（只置「已确认」，状态仍是激活） |
| `zentao_bug_resolve` | **解决 BUG**：提交禅道解决表单（解决方案 / 解决版本 / 解决日期 / 三段式备注），默认 `solve`（已解决）/ `trunk`（主干），提交后回读确认 `status=resolved`（写操作） |
| `zentao_bug_comment` | 追加备注（**只加备注，不改状态**） |
| `zentao_bug_comment_edit` | 修改已发出的备注（写操作；只能改最新一条，见「已知限制」） |
| `zentao_plugin_status` | 注册诊断（取数失败时排障用） |

插件还会注册一段系统提示，告诉模型这些工具何时用、未登录该怎么办。

> ⚠️ 「解决」和「备注」是两条不同的写入路径：禅道里只发备注**不会**把 BUG 变成「已解决」。要「回复三段式并把 BUG 解决」，必须用 `zentao_bug_resolve`（`comment` 放三段式文案，`resolution` 默认 `solve`、`resolvedBuild` 默认 `trunk`），它会回读 `status` 证实；`zentao_bug_comment` 只用于补备注。

## 安装

前置：DeepSeek Harness `0.1.0-rc.2` ~ `0.2.x`（已在 `0.2.0-rc.2`、`0.2.1-rc.1`、`0.2.1-rc.2` 上实机验证；`0.2.1-rc.3` 上已验证工具调用与槽位注册；`0.2.1-rc.5` 上已验证 12 个工具注册、系统提示/HTTP 路由/凭据三个槽位、面板取数与禅道真实连通，并逐项比对过 `@deepseek-ai/dsh-tools` 的导出面（`0.2.0-rc.2` → `0.2.1-rc.5` 无增删、`defineTool` 行为未变））、Node `^22.19 || >=24`。

> ℹ️ **版本声明**：`package.json` 的 `engines.dsh` 写的是 `>=0.1.0-rc.2 <0.3.0`。DSH `0.2.1` 起，第三方插件的兼容性**优先按 `engines.dsh` 判断**（未声明时才回落到旧的 peer 规则），声明不覆盖当前宿主就会被跳过并给出告警。宿主升到 `0.3.x` 时记得同步放宽这个区间。

**方式一：DSH 桌面端插件管理器（推荐）**

设置 → 插件 → 「添加插件」，输入三者之一：

-   **包名**：`@cnbmtech/dsh-zentao-session`（npm 或公司私服）
-   **Git 仓库地址**：内网 Git / GitHub 地址
-   **本地目录路径**：例如 `D:\dsh-plugins\dsh-zentao-session`

> ⚠️ **选「本地目录」时务必先在该目录里执行过一次 `npm install`**。包管理器只做目录链接，**不会**替你安装被链接包自己的依赖；目录里没有 `node_modules` 时插件会因为找不到 `@deepseek-ai/dsh-tools` 而起不来。
> 
> 宿主自带的 `@deepseek-ai/*` 按生态惯例只声明在 `peerDependencies`（本地开发用 `devDependencies` 装进插件自己的目录）——**不写成 `dependencies`**：那样会把钉死的版本塞进 DSH 的共享依赖树、与宿主自己的版本互相干扰（插件清单会告警）。从包名安装时这些包由 DSH 提供，无需额外处理。

**方式二：CLI**

```powershell
# 包名
dsh plugin --profile web add @cnbmtech/dsh-zentao-session
# 本地目录（同样记得先 npm install）
dsh plugin --profile web add D:\dsh-plugins\dsh-zentao-session
```

安装后**完全重启 DSH**（客户端 bundle 只在启动时加载，关开插件不会刷新）。

> ℹ️ **装到旧版本了？**（pnpm 11.7 实测）
> 
> -   pnpm 默认带 `minimumReleaseAge`（24 小时供应链策略），但**显式指定版本**安装时它会自动把该版本写进 `pnpm-workspace.yaml` 的 `minimumReleaseAgeExclude`，无需手动放行。
>     
> -   **刚发布几分钟内**，不带版本号（或 `@latest`）可能因 pnpm 的 registry 元数据缓存而拿到上一个版本。想要立刻拿到新版就**把版本号写全**：
>     
>     ```powershell
>     dsh plugin --profile web add '@cnbmtech/dsh-zentao-session@0.6.8'
>     ```
>     
>     插件管理器里同理，填 `@cnbmtech/dsh-zentao-session@0.6.8`；或者等几分钟缓存过期后重装。
>     
> -   只有 profile 设了 `minimumReleaseAgeStrict: true`（策略变成"必须人工确认"）时，才需要手动加 `minimumReleaseAgeExclude` 条目。
>     

## 登录与配置

打开侧边栏「禅道」面板，填**禅道地址 / 账号 / 密码**，点「登录并保存」——凭据写入 DSH 凭据库 `~/.dsh/.credentials.yaml`（refs：`ZENTAO_BASE_URL` / `ZENTAO_ACCOUNT` / `ZENTAO_PASSWORD`），之后免登录，且可随时「退出登录」清除密码。

也可以在 profile 的 `cordis.patch.yml` 里配置 `id: zentao-session` 的 `config`（优先级：config > 凭证库 > 环境变量）：

| 字段 | 默认 | 说明 |
| --- | --- | --- |
| `baseUrl` | 空 | 禅道地址（不含末尾 `/`）；为空时读凭据 `ZENTAO_BASE_URL` |
| `account` / `password` | 空 | 同上，建议放凭据库，**不要写进配置文件** |
| `timeoutMs` | 30000 | 单次请求超时 |
| `defaultLimit` | 20 | 列表工具默认返回条数 |

未配置地址时插件**不休眠**：照常注册工具与路由，只在你真正取数时提示"请先在面板登录"。

## 侧栏入口：栏上图标 or「更多」

图标**默认显示在左侧栏**（低版本 DSH 没有「更多」按钮，菜单默认会让面板无门——默认上栏是向下兼容的一部分）；高版本 DSH 里点「更多」→ 点那一行的 **「禅道」** 也能开面板。

「更多」里禅道只占**一行**：左边「禅道」（点开面板），右边同一行是「**侧边栏**」三个字 + 一颗**眼睛**，控制这个入口要不要常驻左侧栏—— 睁眼＝图标在左侧栏上，划掉＝收在「更多」里（悬停有提示；写「侧边栏」是为了说清眼睛动的是哪儿）。

-   点这个开关**当刻生效**（不用重启，也不用重载插件）：放出 = 立刻补注册侧栏条目，收起 = 注销那条注册（同时把宿主的入口 pin 一并置为未固定，双保险）
-   那一行是插件自绘的，**不 require 宿主的 client 包**——宿主文档明确不要加载 `@deepseek-ai/dsh-client-ui-primitives` 一类的内部包，组件抛错会让整条槽位条目白屏
-   那一行**全部用内联样式**（颜色只取宿主 `--dsw-alias-*` 令牌并带 fallback）：菜单挂在宿主浮层里，插件样式表在这个位置实测不生效，用类名会退化成浏览器默认按钮
-   选择记在浏览器 `localStorage` 的 `dsh-zentao-sidebar-entry`（`rail` = 栏上常驻，`more` = 收进「更多」，**默认即 `rail`**，2026-11-05 起），换浏览器 / 清缓存后回到默认；低版本 DSH 没有「更多」按钮，图标强制显示在栏上（此时开关不存在）
-   收起后\*\*「更多」里那行「禅道」一定还在\*\*——宿主的「更多」本身不列面板条目（官方入口定制只把面板条目渲染给会话视图与右栏），这一行是收进去之后唯一的入口
-   客户端插件拿不到宿主 YAML 里的 `config`（客户端 `apply` 的第二个参数在运行时是 `undefined`），所以这项偏好不走 `cordis.patch.yml`

图标用的是**禅道官方 logo**（官网上那颗蓝色漩涡标，60×60 透明 PNG，从 zentao.net 页头的 logo 图里裁出来；官方没提供矢量版），以 data URI 内联进 `client.js`，所以插件不额外带资源文件。换浏览器主题时它跟官方一致、不跟着文字变色。禅道与 ZENTAO 是禅道软件的商标，这里仅用于标识本插件连接的是禅道。

## 安全边界

-   面板数据接口 `/api/dsh-zentao-session/*`：**所有 action（含读）都要求自定义请求头** `x-dsh-zentao-session: 1`，且 Host 必须是回环字面量（防 DNS rebinding）
-   截图代理 `/api/dsh-zentao-session-file?path=…`：只放行禅道 `/file-(read|download)-数字` 路径，只回位图/PDF（排除 SVG，避免同源脚本执行面），单文件 8 MB 上限；请求带 `Sec-Fetch-Site` 且不是 same-origin/none 的一律 403（跨站 `<img>` 借宿主会话探测附件的路子封掉，老内核不发该头则保持放行）
-   表单写入：令牌 `kuid` 取不到就**硬失败**，绝不带空令牌出网；非幂等 POST **从不自动重试**（避免重复备注——该版本禅道没有删除备注的入口，只能再编辑一次）
-   贴图与附件写入同样受上一条约束：粘贴图上传（`/file-ajaxPasteImage-<uid>.html`）与带附件的创建提交都要求令牌，请求体上限 32MB，非幂等且不重试
-   `posted: true` 只代表"禅道没报错"；确认/追加备注/改备注都会**回读证实**后才报成功
-   仓库内不含任何禅道地址、账号或密码

## 排障

| 现象 | 处理 |
| --- | --- |
| 面板空白 / 页签缺失 | 客户端 bundle 没重新加载 → **完全重启 DSH**（不是关开插件），再刷新页面 |
| 左侧栏找不到禅道图标 | 默认就在左侧栏；若之前用「更多」里的开关收起过：点左侧栏的「更多」→ 点同一行的眼睛放出。低版本 DSH（没有「更多」按钮）图标强制在栏上 |
| 工具报「请在面板登录」 | 打开侧边栏「禅道」面板登录一次 |
| 取数失败 | 让模型调用 `zentao_plugin_status`，它会打出路由/RPC 注册结果与真实错误 |
| 列表比预期少 | 禅道「我的地盘」一页只回 20 条；面板会标注"服务端共 N 条"，用范围/筛选收窄，或让模型用工具的 `limit` |
| 启动日志出现 `skipping profile bundle "@cnbmtech/dsh-zentao-session"`（面板/工具全都不在） | 宿主判定版本不兼容：`engines.dsh` 没覆盖当前 DSH 版本。升级插件到覆盖该版本的发行；确知无碍时可用 `dsh plugin allow-version` 或插件管理器加"精确版本豁免"，再重启 |

## 已知限制

-   **创建权限按目标探测**：探前 3 个产品/项目（第一个有创建页表单即视为可创建），不逐个探完；权限都在别的目标上时会误显示成"没有创建权限"
    
-   **新建写的是真实禅道**：提交即创建（不可撤销，请在禅道里核对或删除）；附件走禅道自己的 `files[]` 字段，扩展名/大小最终仍由禅道侧规则决定（被拒时弹窗里会显示禅道原话）
    
-   **创建表单的联动是服务端初始态**：切换产品/项目会重新拉取该目标的表单；页面内 JS 联动（如「新建版本」勾选后展开的子字段）以禅道渲染结果为准
    
-   目录/链接安装时依赖需自备（见上）
    
-   列表按禅道分页（服务端一页 20 条）；面板列表**页脚**可翻页、可改每页条数（20 / 50 / 100，页码与总页数取自服务端 pager），模型工具默认取一页再按 `limit` 截断
    
-   **分组是「当前页内」的**（同一条限制的延伸）：组头括号里是本页该组的条数，不是该产品/项目的全量条数；同一产品跨页时会在每页各出一个组头。要按产品整体看，用筛选/搜索，或把每页条数调到 50 / 100
    
-   需求的**详情**接口不返回产品名（只有产品 id），面板会回落到列表行；列表能拿到就没问题
    
-   表单操作只覆盖 BUG（确认/解决/关闭/激活/指派）；任务与需求为只读 + 复制/拖拽/送对话
    
-   需要**禅道账号本身有对应权限**；插件不做越权尝试
    

## 更新日志

从 `0.6.6` 起，每个发布版本都在这里写清「这个版本改了什么」（发版前先写，再 `npm publish`）。更早的版本当时没留说明，只回填了能核实的里程碑。

### 0.6.8 — 2026-11-02

**新功能：列表按「所属产品 / 所属项目」分组、行内显示处理时长，另加面板动效开关**（`0.6.7` 未单独发布，分组一并合入本版）。

-   **列表分组**：组头吸顶一行「所属产品：<名称> (N)」（任务页签为「所属项目：<名称> (N)」），置灰不可点、一个前导不换行空格让它不贴边；BUG 与需求按所属产品、任务按所属项目，无归属的归到末尾「未关联产品 / 未关联项目」
-   列表头「按产品分组 / 按项目分组」开关可切回平铺列表，偏好记在 `localStorage`（`dsh-zentao-group`）；分组作用在**当前页**的行上，组头括号里是本页该组的条数
-   **BUG 行补产品名**：BUG 列表 JSON 只有 product / project 数字 id（实测 56 个字段没有名称），宿主借 `/product-all.json`、`/project-all.json` 的 id→名称表补 `productTitle` / `projectName`（10 分钟缓存；任务行自带名称时不发这次请求），筛选/搜索通道同样补；旧版宿主下由面板自己拉 `targets` 译名兜底
-   **行内处理时长**：右上红色「N 天」+ 其下细进度条，天数 = 创建 → 解决/完成（没走解决直接关闭的回落到关闭时间，未结束的算到今天，只比日期）；承诺天数优先取 `deadline`，没填按 P1=3 / P2=7 / P3=15 / P4=30 兜底；超期铺满并加深字重，悬停写明创建/结束时间、承诺天数来源与超期天数；没有创建时间的行不显示这个标识
-   宿主侧补齐行字段：BUG 行补 `assignedDate / resolvedDate / closedDate / deadline`，任务行补 `openedDate / assignedDate / realStarted / finishedDate / closedDate`（此前任务行连创建时间都没有，无从算时长）
-   **面板动效开关**（顶栏，默认开，记在 `dsh-zentao-motion`）：关掉只是根节点摘掉 `.zts-motion`，DOM 结构与布局不变；开着时换皮肤走 260ms 颜色交叉渐变，极光 / 棱彩的氛围光跟同一个开关
-   **样式热更新**：面板是在同一个页面里反复开关的，旧实现只在 `apply()` 里注入一次 `<style>`，插件更新后样式会冻在旧版本；现在挂载时比对内容并原地替换，关掉再打开面板即可吃到新样式
-   **行为变更：详情全屏改为面板内铺满**，不再调用浏览器 Fullscreen API（ESC 仍可退出）；顺手修正碳纤红分组开关按下态对比度（4.35 → 5.1）
-   测试：新增 `node test-grouping.mjs`（宿主补名缓存与零额外请求、分组开关、样式热更新、旧宿主兜底），`node test-client.mjs` 增加处理时长用例

### 0.6.6 — 2026-10-30

\*\*兼容性发布：适配 DeepSeek Harness `0.2.1-rc.5`，插件代码零改动。\*\*同时把此前一直没发到 npm 的 `0.6.5` 内容一并带上（0.6.4 之后安装的用户会一次拿到）。

兼容性复核（全部实测通过）：

-   `engines.dsh` 声明 `>=0.1.0-rc.2 <0.3.0` 本身就覆盖新宿主，不会被启动时跳过
-   12 个工具注册、系统提示、HTTP 前缀路由（面板取数）、凭据库三个槽位、禅道真实连通全部正常；唯一 `[FAIL]` 的 `rpc.register-failed :: cannot get property "webServer" without inject` 是长期已知项，面板走 HTTP 路由不受影响
-   逐项比对 `@deepseek-ai/dsh-tools` 导出面：`0.2.0-rc.2` → `0.2.1-rc.5` **无增删**（23 个），`defineTool` 行为未变；宿主本版唯一的改动只影响 PTC 嵌套调用的 `presentationMeta`，本插件不使用

随包内容（原 `0.6.5`，此前未发布）：

-   **七套面板皮肤**：原生（跟随宿主明暗）/ 极光 / 波普 / 棱彩 / 青海波 / 记分牌 / 碳纤红，切换即时生效并记忆在 `localStorage`
-   **「按角色处理」**：开发 / 测试 / 产品 / 管理四套任务书，一键在项目里新建对话并发送「身份开场白 + 条目引用 + 任务书」（开发那套是四步流程，含 `zentao_bug_resolve` 三段式回写）
-   **详情全屏**：贴边铺满，退出后列表/筛选/滚动位置复原
-   **工具栏操作组**：新建 / 角色 / 状态筛选同 32px 基线、组内 8px 间距
-   **搜索与筛选口径统一**：筛选态走 query 通道，页脚分页、页签计数、统计卡与主卡口径一致
-   面板**不再定时自动刷新**（0.6.4 的「自动刷新」下拉已移除），改为操作后与手动刷新

本地开发：

-   `devDependencies` 对齐宿主：`@deepseek-ai/dsh-tools` `0.2.1-rc.5`、`@deepseek-ai/schemastery` `3.18.5`（只影响目录 / `link:` 安装的本地开发，npm 使用者用宿主自带那份）

### 历史里程碑（回填，日期为 npm 发布时间）

| 版本 | 日期 | 内容 |
| --- | --- | --- |
| 0.2.0 | 2026-10-15 | 首发：9 个工具（我的 BUG / 任务 / 需求、BUG 与需求详情、BUG 确认 / 备注、注册诊断）+ 侧栏面板（页签切换、拖拽与复制、送对话、详情全屏、图片画廊、搜索） |
| 0.3.0 | 2026-10-15 | 面板加「新建」表单与列表分页（页码/每页条数取自服务端 pager） |
| 0.4.1 | 2026-10-17 | 新增 `zentao_task_view`、`zentao_bug_comment_edit`（工具 9 → 11）；新建表单支持富文本贴图与附件 |
| 0.5.1 | 2026-10-19 | 新增 `zentao_bug_resolve`（工具 11 → 12）：三段式备注 + 解决表单一次提交并回读状态 |
| 0.6.4 | 2026-10-26 | 侧栏入口收进左侧栏「更多」菜单（同行开关可切回栏上） |
| 0.6.6 | 2026-10-30 | 适配 DSH 0.2.1-rc.5，并补发 0.6.5 的面板内容（见上） |
| 0.6.8 | 2026-11-02 | 列表按所属产品 / 所属项目分组、行内处理时长、面板动效开关（0.6.7 未单独发布，已合入本版） |

> 同一阶段内的补丁版本（0.2.1–0.2.3、0.3.1–0.3.5、0.4.2、0.5.3–0.5.6）当时没留说明，只保留了包本身，不再逐条回填。

## 开发与自测

```sh
npm install
node test-client.mjs          # 面板全链路（最小 React hooks 模拟宿主）
node test-tools.mjs           # 11 个工具的渲染 + lossless JSON
node test-form-encoding.mjs   # 写路径铁律（多选重复键 / 302 不重发 / 缺 kuid 不出网）
node test-real-render.mjs     # 真实 React SSR 首跳
node test-real-browser.mjs    # jsdom + 真实 React 全交互链
node test-sidebar-entry.mjs   # 侧栏入口：默认上栏（低版本兼容）、开关收放当刻生效 + 低版本回退
node test-skins-and-fixes.mjs # 六套换色皮肤（+ 原生 = 七项）+ 切范围同步刷新 + 搜索清空
node test-grouping.mjs        # 列表分组：宿主补产品名/项目名（缓存/零多余请求）+ 面板按产品/项目分组与开关
node test-client.mjs          # 同一份里含「列表行处理时长」用例：8 天/15 天=53%、超期铺满、无日期不渲染
```

面板皮肤（全部七套）另有一套像素级验收脚本，放在仓库工作区的 `ui-mocks/` 下 （`SKINS=popart,ukiyo node panel-shot.mjs` 可只跑指定皮肤，不传则全跑）： `panel-harness.html`（浏览器内加载真实 `client.js` + 真实 React 18 + 真实禅道数据）、 `panel-shot.mjs`（无头 Chrome 出图 + 真实像素对比度 + 布局体检）、 `panel-clip-probe.mjs`（窄栏裁剪/滚动可达性定点探针）、 `panel-verify-3fix.mjs`（顶栏/工具栏/页签/统计卡口径的行为断言）、 `verify-interactive.mjs`（真浏览器：CDP 真实鼠标点搜索清空 ×、Esc 清空、切范围后 query/counts 都带新范围且主卡/子卡/页脚口径一致、皮肤切换即时生效与记忆、× 在 7 套皮肤下常规态与悬停态的对比度）、 `verify-fullscreen.mjs`（真浏览器 × 7 套皮肤：全屏后顶栏/页签/统计卡/工具栏必须全部消失、详情卡贴边铺满 1440×950、有退出入口、退出后四层复原；以及工具栏四控件 32px 同基线/圆角/组内 8px 间距）。

目录结构：`index.js`（宿主：工具 + HTTP/RPC 路由 + 系统提示）、`src/client.js`（宿主侧禅道客户端：登录/列表/详情/表单）、`client.js`（浏览器面板 bundle）、`cordis.patch.yml`（bundle 安装层）。

## 发布与迭代（维护者）

```powershell
# 0. 先在 README「更新日志」最上面写这次改了什么（下面的模板），再发版
npm login                                   # 首次：需要 npm 账号，且对 @cnbmtech 组织有发布权
npm version patch                           # 0.2.0 → 0.2.1（或 minor/major）
npm publish --registry=https://registry.npmjs.org   # 本机默认 registry 是 npmmirror 镜像，发不上去
npm view @cnbmtech/dsh-zentao-session version --registry=https://registry.npmjs.org   # 确认 latest 已更新（大约 1 分钟）
```

**更新日志模板**（`README.md` 的「更新日志」节，新版本写在最上面）：

```markdown
### <版本> — <YYYY-MM-DD>

**<一句话：这个版本为什么发（兼容性 / 新功能 / 修复）>**

- <用户能感知的变化，一条一句；兼容性发布要写清核对过什么、有没有改代码>
- <已知问题 / 行为变更（比如移除了哪个开关）也要写，别只说新增>
```

-   **首次发布前**需要在 npm 上创建组织 `cnbmtech`（免费组织只能发公开包），或用公司私有源： `npm publish --registry https://私服地址`（并让同事的插件管理器指向该源）。
-   **本机开发**不必发布：profile 里以 `link:` 指向源码目录，改完代码**重启 DSH** 即生效；只有同事需要走 npm。
-   **同事升级**：插件管理器里重新添加同名包（或 `dsh plugin --profile web update @cnbmtech/dsh-zentao-session`），再完全重启 DSH。
-   发布物只含 10 个文件（`package.json` 的 `files` 白名单），不含 `node_modules`、测试脚本与任何凭据——发布前可用 `npm pack --dry-run` 复核。

## 许可

MIT

## 常见问题

### @cnbmtech/dsh-zentao-session 是什么？

ZenTao \(QQ\) 基于会话的 DeepSeek Harness 工具和面板: 我的bug, 任务和故事横跨浏览范围, bug 确认/解析, 评论文章和评论编辑, 加上拖到聊天的 Markdown 引用\. 与缺少 REST API 的遗留 ZenTao 版本一起工作

### 如何安装 @cnbmtech/dsh-zentao-session？

在项目目录运行 npm install @cnbmtech/dsh-zentao-session，或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖：react（^18\.2\.0）、@deepseek-ai/cordis（\>=4\.0\.0）、@deepseek-ai/dsh-tools（\>=0\.1\.0-rc\.2 \<0\.3\.0）、@deepseek-ai/schemastery（\>=3\.18\.0）。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖，Yarn 需要手动安装。

### @cnbmtech/dsh-zentao-session 支持 TypeScript 吗？

该包的 package\.json 没有声明类型入口。需要类型提示时，可以在 npm 上查找社区维护的 @types/cnbmtech\_\_dsh-zentao-session 是否存在。

### @cnbmtech/dsh-zentao-session 支持 ESM 还是 CommonJS？

以 ES Module 形式发布，推荐使用 import 引入。在 CommonJS 项目中，Node\.js 20\.19\+ / 22\.12\+ 可以直接 require\(\) 不含顶层 await 的 ES Module，更早的版本需要改用动态 import\(\)。

### @cnbmtech/dsh-zentao-session 需要什么版本的 Node\.js？

package\.json 的 engines 字段要求 Node\.js ^22\.19\.0 \|\| \>=24\.0\.0。版本不符时 npm 默认只给出警告，开启 engine-strict 后会拒绝安装。

### @cnbmtech/dsh-zentao-session 可以商用吗？

@cnbmtech/dsh-zentao-session 使用 MIT 许可。宽松许可：允许商业使用、修改和再分发，通常只需在分发时保留原版权与许可声明。以上为概括，不构成法律意见，请以许可原文为准。

### @cnbmtech/dsh-zentao-session 的下载量有多少？

根据 npm 官方下载统计，@cnbmtech/dsh-zentao-session 在 2026/10/10 – 2026/11/08 这 30 天内被下载 2,346 次，日均约 78 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入，适合横向比较热度，不能直接等同于使用人数。

### @cnbmtech/dsh-zentao-session 还在维护吗？

最新版本 0\.6\.9 发布于 2026/11/05（5 天前）；至今共发布 22 个版本，其中过去 12 个月发布了 22 个；首次发布于 2026/10/15。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

