dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
在 DSH Web GUI 左侧栏底部的动作区里放一块「进行中」坞:把正在跑、卡在等你处理和刚跑完还没看过的会话收成一张小列表,点一行就把主视图切过去;没有这类会话时收成一行细字,只留「暂无进行中」。坞是布局里的真实一块,排在这排动作的最上面,不浮在会话列表上。
官方侧栏本来就会在会话行上标运行状态点(StateDot)和待处理状态,但那些行分散在各个工作区分组里 —— 组一折叠就看不见,要判断「现在有几个会话在等我」得逐个展开找。
这个坞不新增数据源,只做聚合:复用同一套标准 Hook(useSessions / useSessionStatus),筛出 running、pending 或 completionUnread 的会话,按最近更新时间排,固定在侧栏底部动作区。它不轮询、不落盘、没有 Host 状态,也不改任何会话。
想知道 · 看 怎么装、怎么用、怎么排错 · 本文件 为什么这么实现(槽位取舍、过滤规则、空态、locale 降级) · IMPLEMENTATION.md 给 agent 的约束、命令、改动连带项 · AGENTS.md
本仓库是 DSH 独立插件仓库,当前只有这一个插件;仓库根就是插件包 —— package.json、index.js、client.js、cordis.patch.yml、locale/ 都在根目录,装哪个 profile 都指到仓库根。
能力 · 说明 进行中聚合 · running === true 的会话进坞,行尾「运行中」 待处理聚合 · 有 pendingInteraction 的会话进坞,按类型显示「等待审批 / 计划待审 / 等待回答」;待处理优先于运行中 完成未读聚合 · completionUnread === true(主视图之外停了运行、结果还没看过)的会话进坞,行尾灰字「已完成」;打开该会话后官方清掉这个标记,行随之消失 一键切换 · 点行调用 uiWorkspace.openSession,主视图切到该会话 计数徽标 · 坞头右侧显示当前行数 头部状态点 · 只要还有会话在跑或在等你处理,绿点就脉冲;只剩「已完成」提醒时切成静止的 6px 成功色圆点,不再假装有任务在跑 空态细行 · 无进行中会话时收成一行无边框细字,灰点静止,不显示计数 中英双语 · 内置 zh / en 两套文案,跟随界面语言;locale 服务缺失时回落中文 仅 Web 客户端 · dsh.client.platform: web;headless 等没有 Web 客户端的 profile 不加载它 占位不遮挡 · 注册在 sidebar.footer.action(侧栏底部动作区),是布局里的真实一块,不盖住会话列表 与官方入口共存 · 坞独占动作区最上面一行,用量统计 会话管理 等原有入口照旧排在它下面,一个都不少 键盘可达 · 每行是 `,可 Tab 聚焦,Enter / Space 触发,:focus-visible` 有品牌色描边
不提供:停止运行、回答提问 / 批准审批、归档过滤、设置项 / 开关。那些动作属于官方浮层,坞刻意不复制第二套交互。
四步:装 → 确认装配行 → 用 → 卸载。
target 就是仓库根(package.json、index.js、client.js、cordis.patch.yml、locale/ 都在根目录)。 2026-10-25 之前包体塞在同名子目录 running-sessions-float/ 里,github: 直装和 #tag 钉版本全都装不出来 (pnpm 把没有 package.json 的根当空壳,依赖名落成 running-sessions-float#0.1、版本 0.0.0,随后 dsh plugin add 报 cannot resolve profile bundle);那层子目录已经删掉,现在三种装法都指仓库根。
DSH Web → 左侧栏 插件 → 添加插件 → 填仓库根的绝对路径(在仓库根执行 realpath .)→ 安装 → 装完点 立即启用。
仓库地址(clone 用):https://github.com/dongasai/running-sessions-float.git
dsh plugin --profile web add github:dongasai/running-sessions-float
# 或
dsh plugin --profile web add git+https://github.com/dongasai/running-sessions-float.git
# 钉版本(URL 后加 #<tag>,锁到某个发布版本来复现问题 / 稳定使用)
dsh plugin --profile web add github:dongasai/running-sessions-float#0.3dsh plugin --profile 把参数转发给该 profile 目录里的 pnpm。装进来的是副本:之后改本仓库代码不影响已装的副本(想边改边用就走方式三)。
--profile 必填(不写直接报 required option '--profile not specified')。git@github.com:owner/repo.git 这种 scp 写法不会被认成 git spec,pnpm 会把它当路径,装出一条指向不存在目录的 link: 死链。#(上面第三条就是钉 0.3),tag / 分支 / commit 都行。本仓库当前 tag 是 0.3,对应 package.json 的 0.3.0 —— 不带 # 则跟 main 走,每次装到的都可能是新代码。0.2 及更早的 tag 是「包在子目录」的旧布局(0.1 还是旧包名 @local/running-sessions-float),钉它们装不出当前这套,只能走「克隆 + checkout」。prepare 等依赖构建脚本;本包没有任何构建步骤(client.js 是手写的浏览器 bundle,由 Host 直接读盘发出),不会遇到。本机实测(0.3,临时 `DSH_HOME`):github:dongasai/running-sessions-float#0.3 装完依赖名是 @dongasai/running-sessions-float github:dongasai/running-sessions-float#0.3(不是旧布局那种 running-sessions-float#0.1 0.0.0),副本 package.json 版本 0.3.0,dsh.profile.bundles 自动登记,--dump-config 能查到装配行(见下面「装完确认」)。
# 在仓库根执行;--profile 必填,web 换成你要装进的 profile
dsh plugin --profile web add "link:$(pwd)"dsh plugin --profile 把参数转发给该 profile 目录里的 pnpm。link: 装的是软链:改仓库里的 client.js,客户端半边会在一两秒内换新(见「生效条件」)。
这条命令要写 profile 目录,在宿主 shell 里跑;受限的 Agent 沙箱通常写不了 ~/.dsh。
让 Agent 装(等价路径,不是命令行):action: install_bundle、target: link:(或 github:dongasai/running-sessions-float#0.3)。这个动作需要 danger-full-access 批准(它会改 profile、装包、可能跑安装脚本);返回 application: applied 才算当场生效,restart-required 表示要重启 Harness。
tag 0.3 真实存在,对应 package.json 的 0.3.0。想连 git 历史一起钉住时这样装:
git clone https://github.com/dongasai/running-sessions-float.git
cd running-sessions-float
git checkout 0.3
dsh plugin --profile web add "link:$(pwd)"0.2 及更早的 tag 里包还在子目录,realpath 要指到 /running-sessions-float(那一层已在新版删掉)。
包名是 `@dongasai/running-sessions-float`(2026-10-23 从 @local/running-sessions-float 改过来,与本机其它自研插件同一 scope)。改名是破坏性的:
0.1 及更早的 tag 与 main 上 2026-10-23 之前的提交里,package.json、cordis.patch.yml、client.js 的 client id 都还是 @local/...;装那些版本得到的插件页条目与装配 id 都是旧名。dsh.profile.bundles 选中项、node_modules/@local/running-sessions-float 软链都指向旧名。按下面「换包名时怎么升」重装一次即可。换包名时怎么升(本机实测流程):
# 1) 移除旧名(先卸再装,避免两个名字同时被装配)
dsh plugin --profile web remove @local/running-sessions-float
# 2) 用当前仓库装新名
dsh plugin --profile web add "link:$(pwd)"
# 3) 确认依赖名、bundles 选中项、软链三处都换了
grep -n running-sessions-float ~/.dsh/profiles/web/package.json
ls -l ~/.dsh/profiles/web/node_modules/@dongasai/running-sessions-float
# 4) 确认装配行(应显示 @dongasai/running-sessions-float)
dsh --profile web --dump-config | grep -A2 running-sessions-float
# 5) 重启正在跑的实例(插件行在启动时合成)pnpm 移除链接包时不一定会清掉旧 node_modules/@local/... 软链 —— 那条孤立软链不在 loader entries 里、不会被加载,但要留意将来 pnpm install 的歧义。确认它指向本仓库这个包再删(realpath 核验后再 rm,别删成别的插件的链接)。
命令行可以直接看合成后的 profile 树(只读,不启动服务):
dsh --profile web --dump-config | grep -A2 running-sessions-float期望输出(本机实测):
# == @dongasai/running-sessions-float
- id: running-sessions-float
name: '@dongasai/running-sessions-float'运行中的实例要重启才会加载它(插件行在启动时合成)。
五条通用坑:
--profile 必填 —— 不写直接报 error: required option '--profile ' not specified。dsh.profile.bundles = 装了不挂载 —— 命令行的 dsh plugin add 写的是依赖,挂载还取决于 bundles;实测清空 bundles 后 --dump-config 输出为空,所以装完必须用上面的命令验一遍。github: / git+ 前缀 —— git@github.com:owner/repo.git 这种 scp 写法会被 pnpm 当成本地路径,装出一条指向不存在目录的 link: 死链。0.3(对应 0.3.0)。0.2 及更早的 tag 是「包在子目录」的旧布局、0.1 还是旧包名 @local/running-sessions-float,钉它们装不出当前这套;那些版本只能「克隆 + checkout + link 子目录」。package.json 时 pnpm 会把仓库当空壳(依赖名落成 running-sessions-float#0.3、版本 0.0.0),dsh plugin add 随后报 cannot resolve profile bundle。2026-10-25 已按这条把结构改回「根即包」。本仓库 private: true、未发布 npm,没有 registry 装法;package.json 里没有声明任何 DSH peer 依赖,不会触发版本兼容闸。
装完等一两秒 —— 客户端半边在启动时加载,Web 组合会通过 client-hmr 把新模块推给页面;没出现就刷新一次页面。侧栏底部、设置行上方出现坞:
updatedAt 从新到旧排;刚发过消息的会话在最上面。min(28vh, 200px),给下面的官方入口和设置行留位置)。--dump-config 里能查到装配行(命令与期望输出见「快速开始 → 装完确认」)。completionUnread,这行应当消失。看不到坞时按「排错」表逐条对。
Web 插件页:停用这个插件 → 移除。或让 Agent 执行:
action: remove_bundle
target: @dongasai/running-sessions-floatremove_bundle 会先取消 bundle 选中、再删包。不要只跑 `pnpm remove`:依赖没了、dsh.profile.bundles 里的选中项还在,下次装配会解析不到这个包。
侧栏(默认 280px,最窄 264px)
┌────────────────────────────┐
│ 工作区 A │
│ · 会话 1 │
│ 工作区 B │
│ · 会话 2 │
│ · 会话 3 │
│ │
│ ┌──────────────────────┐ │ ← 坞:侧栏底部动作区顶部
│ │ 进行中 3 │ │ 独占一行,排在官方动作之上
│ │ 会话 2 运行中 │ │ 最高 min(28vh, 200px)
│ │ 会话 3 等待审批 │ │ 列表在坞内滚动
│ │ 会话 1 已完成 │ │ 跑完但还没看过的也在这里
│ └──────────────────────┘ │
│ 用量统计 │ ← 原有入口照旧,排在坞下面
│ 会话管理 │
│ [设置] │
└────────────────────────────┘逐块说明:
--dsw-alias-state-success-primary;待处理用 --dsw-alias-state-warn-primary;已完成用次级文字色 --dsw-alias-label-tertiary(它是提醒,不是告警,不抢眼);其余文字 / 边框 / 背景都走主题 token,明暗主题都跟随。padding: 2px 6px 的一行 11px 次级色文字。sidebar.footer.action 槽位,order: -10,排在所有官方底部动作之前;width: 100%,高度由内容决定,最高 min(28vh, 200px)。它占真实布局:坞变高时上方会话列表相应变矮,而不是被盖住。flex-direction: row,而这条生态里的底部入口(用量统计 / 会话管理 / 搜索)都写着 flex: 0 0 100%,意思都是「我要占满一整行」。不换行时第一个就把别人挤出可视区。坞挂载时给那一行加 flex-wrap: wrap(promoteFooterContainer,另有一条等价 :has() 规则管首帧),于是每个入口各占一行、都在坞下面。flex-direction: column —— 列里 100% 会按高度算,搜索插件那个入口直接涨成整块 140px,把别人顶出屏幕。只有「可换行的行」同时满足它们的 100% 宽度语义。wide: false,坞换成 36px 圆点 + 计数的圆片(rsf-rail),不摊开列表。会话目录与运行状态都来自框架标准 Hook,插件自己不做任何请求:
条件 · 是否进坞 · 行尾状态 有 pendingInteraction · 是 · 按 kind 归类(见下表),优先于运行中与已完成 running === true 且无待处理 · 是 · 运行中 completionUnread === true 且已停下 · 是 · 已完成(灰字) blank === true(新建但还没发消息) · 否 · — origin === 'subagent' · 否 · — 其余空闲会话(含已看过结果的结束会话) · 否 · —
三个保留条件之间是并集,显示优先级是 待处理 > 运行中 > 已完成:一个会话既在跑又有待审批时显示「等待审批」,刚跑完但还没看过时显示「已完成」。
running 优先取状态快照;状态里没有这个字段(或没有该会话的状态)时,退回列表快照的 summary.running。completionUnread 只从状态快照读,缺字段就当作「已读过」:列表快照里没有对应事实,猜一个会让坞凭空多出一行「已完成」。官方在主视图里停下运行时不会置这个标记,所以坞里出现的每一条「已完成」都是你没在当前视图里看过的。kind 是各域自己定的字符串,插件只做大小写无关的包含匹配:kind 实际值 · 坞里显示 approval · 等待审批 permission(当前没有发布者,兼容用) · 等待审批 plan-review · 计划待审 question · 等待回答 其它 / 未知 · 等待回答
官方目前会发布 approval、plan-review、question 三种;未知 kind 不会让会话消失,统一落到「等待回答」。
本插件 Host 半边是空的(index.js 只有一个空 apply()),没有 Host 状态、路由或配置,所以不存在「Host 改了要重启」的问题;所有行为都在浏览器半边。
link:,改 client.js:Web 组合里的 client-hmr 每 500ms 轮询 bundle 文件的 mtime / ctime / size,变了就换新 revision 并通过 /plugins/events 推给浏览器;通常一两秒内自动换新,不用刷新。没换再刷新页面。client-hmr:Host 内存里还是旧字节,刷新页面也不够 —— 重新安装或重启 Harness 才会重读。package.json 的 dsh.client / dsh.bundle 字段:需要重新装配(重新安装或重启对应 profile)。running-sessions-float/ ← 仓库根 == 插件包(安装 target)
├─ package.json # name / version / exports / files / dsh.bundle + dsh.client
├─ cordis.patch.yml # insert 一行插件入口
├─ index.js # Host 半边:export function apply() {}(8 行)
├─ client.js # 浏览器半边:CSS + 组件 + sidebar.footer.action 注册(477 行)
├─ locale/en.json # 插件页元数据(meta.title / meta.description)
├─ locale/zh.json # 同上,中文
├─ icon.svg # 插件图标
├─ image/1.png # README 顶部的预览截图(文档资产,不进 npm files)
├─ README.md
├─ IMPLEMENTATION.md
├─ AGENTS.md
├─ test/smoke.mjs ← 无 DOM 冒烟 + 包契约(42 项)
└─ .agents/index.json ← 架构扫描状态:模块清单 / 覆盖率 / 缺口README.md / IMPLEMENTATION.md / AGENTS.md / test/ / .agents/ 靠 package.json 的 files 白名单挡在包外,只进 git。
文件 · 行数 · 作用 package.json · 32 · 包声明:dsh.bundle.patch 指向 patch,dsh.client 声明 web 平台、启动即加载、依赖 session UI 包 cordis.patch.yml · 3 · insert 一行 running-sessions-float → @dongasai/running-sessions-float index.js · 8 · Host 半边,空 apply();不提供服务、不注册工具、不挂路由 client.js · 477 · 浏览器半边:样式、组件、locale 注册、sidebar.footer.action 注册 locale/*.json · 6 × 2 · 只给插件页的标题 / 描述;界面文案在 client.js 的 DICTS 里 icon.svg · 11 · 插件图标 AGENTS.md · — · 给 agent 的命令、硬约束与改动连带项 test/smoke.mjs · 324 · 无 DOM 组件冒烟 + 包契约(42 项)
inject。index.js 只导出 apply(),不依赖任何 Host 服务。client.js 模块返回):inject: ['slots'];apply(ctx) 里 ctx.get('locale') 与 ctx.slots.inject('sidebar.footer.action', ...);uiWorkspace 不在这里取,而是每次点击时现取(它由启动更晚的 workspace 插件补挂)。package.json → dsh.client.inject):@deepseek-ai/dsh-client-ui-session —— 它提供 useSessions / useSessionStatus 这两个全局标准 props,必须排在它之后激活。immediately: true,启动时就加载,不等打开某个面板。扩展点 · 类型 · id / 命名空间 · order · priority · patterns · 条件 · 说明 sidebar.footer.action · list 槽位 · running-sessions-float · -10 · 未设 · 无(该槽没有 patterns) · 无条件 · 唯一 UI 注册;list 槽按 order 升序渲染,负值排在官方动作(默认 0)之前 locale · 字典命名空间 · local.runningSessions · — · — · — · locale 服务存在时 · 注册 en / zh 各 8 个 key(title running approval plan answer done empty openHint);不在时用内置中文 uiWorkspace · 可选服务 · — · — · — · — · 每次点击 · 点击时才 ctx.get('uiWorkspace') 并调 openSession;不要在 `apply` 时缓存 —— 它由启动更晚的插件提供,缓存到的是 undefined;缺失则点击无动作
不占 sidebar / sidebar.workspaces.* / composer.* 等其它槽位;sidebar.footer.action 是本插件唯一的 UI 注册。