dsh-float-lamp
悬浮任务状态灯:独立于 DeepSeek Harness 窗口的置顶小灯,实时显示任务状态;鼠标悬停显示正在运行的任务信息。
A floating, always-on-top status lamp that lives outside the DeepSeek Harness window: 🟢 running, 🟠 needs approval, 🔵 needs your input, ⚪ idle, dark-grey host offline. Hover the lamp to list running tasks. Windows only (PowerShell WinForms).
灯色含义
| 灯色 | 含义 | 判定 |
|---|
| 🟢 绿 | 运行中 | 有会话 turn/start 未闭合 |
| 🟠 橙 | 待你授权 | 有 approval/asked 未 approval/decided |
| 🔵 蓝 | 待你交互 | ask_user_question / exit_plan_mode 调用挂起 |
| ⚪ 灰 | 空闲 | 无活动轮次 |
| ⚫ 暗灰「宿主离线」 | DSH 未运行 | state.json 超过 15 秒未更新,或标记 hostDown |
- 多会话聚合优先级:橙 > 蓝 > 绿 > 灰;灯上显示当前状态与计数,绿灯时附带本回合已运行时长(分/秒)。
- 极简发光 + 动画:无卡片背景(
UpdateLayeredWindow 逐像素 alpha,边缘抗锯齿无暗边),只有发光圆灯(三层光晕+高光)
和带暗色描边的文字,任何桌面背景下都可读——
🟢 灯体静止,周围光晕呼吸(忽亮忽暗、轻微涨缩);
🟠 左右紧张抖动;🔵 上下漂浮;⚪/⚫ 完全静止。
动画 20fps,仅绿/橙/蓝状态运行(不闪不晃,不打扰)。
- 悬停卡片:鼠标移到灯上约 0.3 秒,灯左侧弹出深色卡片,逐行列出每个会话:
[状态] 最近一条用户消息(正在: 工具名);移开自动消失,拖动灯时隐藏。
- 交互:左键拖动记忆位置;轻点灯(未拖动)跳回 DSH 宿主窗口(最小化会自动还原);
右键菜单:置顶开关 / 动画开关 / 透明度(100/75/50/25%)/ 大小(小/标准/大)/ 退出,
设置记忆在
settings.json。高 DPI 自动适配(PerMonitorV2,几何与字体按缩放系数放大)。
- 离线超过 60 秒灯自动退出(宿主重启后会重新拉起)。
原理
lib/index.js(宿主插件):
- 权威路径:每 800ms 轮询
ctx.sessions.list(),对每个会话从游标起增量折叠
session.log 里的 turn/start|end、approval/asked|decided、tool/call|result、
user/message(悬停标题),按 seq 去重。
- 快速路径:同时订阅
session/created / session/event 与 internal/dispatch(global),
事件到达即时更新(双投递由 seq 去重兜底)。
- 聚合结果原子写入
~/.dsh/float-lamp/state.json(5 秒心跳 + 事件即时更新),
并自动拉起灯进程(单实例:lamp.pid + lamp-alive 心跳探活;
进程存活但心跳停止视为僵尸灯,先收割进程再重拉新灯)。
- 自禁用检测:心跳时读取
~/.dsh/profiles/<profile>/cordis.patch.yml,
若本插件被标记 disabled: true,立即写 hostDown 并杀掉灯(约 5 秒内熄灭);
重新开启则重新拉起灯。
apply() 返回清理函数(cordis fiber disposable)。⚠️ 不要在 apply 里访问
ctx.dispose——Context 没有该成员,代理会抛错并使 fiber 进入错误态(所有监听被摘除)。
widget/lamp.ps1(独立灯窗):PowerShell WinForms,置顶无边框(184×96 × DPI/大小系数),
UpdateLayeredWindow 逐像素 alpha 全透明渲染(抗锯齿边缘、可调整体透明度);
400ms 轮询 state.json;animTimer 50ms/帧仅在
running/approval/question 且动画开启时驱动(相位角驱动光晕呼吸、抖动、漂浮)。
左键拖动(位置记忆到 pos.json)/ 轻点跳宿主窗口,右键菜单:置顶 / 动画 / 透明度 / 大小 / 退出。
互斥锁 + lamp.pid + lamp-alive 心跳保证单实例(环境变量 DSH_LAMP_TAG 可换锁名,供测试实例并存);
退出/离线自动退出均硬杀进程([Environment]::Exit(0)),tick 异常记入 lamp-error.log。
安装 / Install
dsh plugin --profile desktop add github:shZhang1614/dsh-float-lamp
然后重启 DeepSeek Harness 应用;插件加载后会自动拉起灯窗(无需手动运行任何脚本)。
手动安装(不经过 dsh plugin 命令)
- 把源码放到任意目录,例如
~/dsh-plugins/dsh-float-lamp。
- 在 profile 的
~/.dsh/profiles/<profile>/package.json 中:
- dependencies 加
"dsh-float-lamp": "link:<源码目录的绝对路径,正斜杠>"
dsh.profile.bundles 数组追加 "dsh-float-lamp"
- 创建 junction:
mklink /J "%USERPROFILE%\.dsh\profiles\<profile>\node_modules\dsh-float-lamp" "<源码目录>"
- 重启 DeepSeek Harness 应用。
离线分发(把 zip 发给朋友)
本插件零依赖,可完全离线安装:把发布 zip(含本仓库全部文件)发给朋友,对方只需:
- 解压到任意文件夹(如
d:\dsh-float-lamp);
- 在该文件夹里运行
install-offline.ps1
(右键 → 使用 PowerShell 运行,或 powershell -NoProfile -ExecutionPolicy Bypass -File install-offline.ps1);
- 完全退出并重新打开 DeepSeek Harness(托盘图标右键退出)。
脚本会自动复制插件到 ~/dsh-plugins/dsh-float-lamp、写入 profile 配置、创建 node_modules junction。
卸载运行 uninstall-offline.ps1 即可。要求:Windows(自带 PowerShell 5.1+)、已安装并能启动过 DeepSeek Harness。
开关插件
- 在 dshmarket 里开关:会改写 profile 的 bundles 与
cordis.patch.yml(disabled: true)。
- 插件处于加载状态时,开关约 5 秒内实时生效(关闭→灯自动熄灭)。
- 关闭后下次重启不再加载插件;重新开启后需重启应用才会再次加载并亮灯。
手动操作
- 手动启动灯:
powershell -NoProfile -ExecutionPolicy Bypass -File widget\lamp.ps1
- 禁止插件自动拉起灯:环境变量
DSH_FLOAT_LAMP_NO_WIDGET=1
- 单独运行灯(无宿主):灯显示「宿主离线」,60 秒后自动退出,属正常。
- 悬停卡片自动弹出测试:环境变量
DSH_LAMP_TEST_TIP=1(启动后首个 tick 直接弹卡片)。
文件一览(运行时目录 ~/.dsh/float-lamp/)
| 文件 | 作用 |
|---|
state.json | 聚合状态(state、counts、runningSince、tasks、updatedAt、pid、hostDown) |
pos.json | 灯窗位置记忆 |
settings.json | 灯窗偏好(opacity、scale、animation) |
lamp.pid | 灯进程 pid(单实例检查) |
lamp-alive | 灯进程 2 秒心跳(插件据此判活,僵尸灯自动收割重拉) |
lamp-error.log | 灯进程 tick 异常记录(如有) |
debug.log | 插件诊断日志(加载、订阅、轮询错误、spawn) |
注意 / 已知坑
widget/lamp.ps1 必须保存为 UTF-8 带 BOM,否则 PowerShell 5.1 按 GBK 解析中文报错。
- 拉起灯不能用
detached: true(powershell 会立即退出);用 stdio:'ignore' + windowsHide + unref。
- WinForms
ToolTip 在无边框置顶窗上不会弹出,悬停卡片是自绘的 LampTipForm
(Add-Type 需 -ReferencedAssemblies System.Windows.Forms)。
- PowerShell 5.1(.NET Framework)没有
[System.Drawing.Point]::From,右键菜单要用
$menu.Show($form, $e.X, $e.Y) 重载,否则右键弹未处理异常错误框。
- 应用内「重启」若被活跃会话阻塞,会出现新宿主反复启动失败的循环;等当前任务结束或从托盘完全退出后重开。
卸载
在 dshmarket 关闭(或从 profile package.json 移除 dependency 与 bundles 条目),
删除 node_modules\dsh-float-lamp junction 与源码目录,重启应用。
测试
node test/fold-test.mjs — 状态机 19 项断言(四态、优先级、多会话、重放播种、harness 噪音过滤、运行时长)。
GitHub Actions 每次 push 自动运行。
- 假 ctx 冒烟:apply 返回清理函数、poll 折叠事件、cleanup 写 hostDown。