packages/dsh-task-board
zhu1090093659/dsh-web侧边栏多列任务看板:卡片交给真实 DSH 智能体会话执行,支持 cron 定时(Host 侧到点执行,关浏览器也生效)。
更换 DeepSeek Harness 桌面端应用图标(exe、托盘、开始菜单),支持上传任意 .ico,并可随时还原官方默认图标。
【本插件代码由 DeepSeek Harness 生成】
DeepSeek Harness 桌面端的应用图标更换器插件:把官方 exe 内嵌的图标换成你自己的 .ico,也能一键回退成官方默认。
宿主半区(
lib/index.js,跑在 DSH 主进程里)通过ctx.webServer挂一组 API; 客户端半区(lib/client.js)往设置页注册一张卡片,所以不需要手输 URL。 两侧都不引入任何 npm 依赖。
Windows 会锁住正在运行的可执行文件,而这个插件就跑在它要改的那个 exe 里。 所以 API 不会当场动手,而是:
state.json 与 pending-job.jsonWin32_Process.Create)拉起 worker —— 关键点:直接 spawn 的子进程会随应用被 job object 一起干掉,而 WMI 创建出来的进程挂在 wmiprvse.exe 名下,不受应用 job object 约束,能在应用退出后继续活着bin/apply-icon.ps1)轮询等待 exe 被释放ie4uinit.exe -show,并把 pendingExplorer 写进 state.json;
应用重启后宿主侧 reconcilePendingExplorer() 会调 bin/refresh-shell.ps1 做完整刷新:
ie4uinit -show → 停 Explorer → 抢删 iconcache_16/32/48/256/wide/idx.db → 起 Explorer → 广播 SHCNE_ASSOCCHANGED也就是说:改图标必然伴随一次应用重启,这是平台限制,不是偷懒。
选择 前缀)。
本机真踩过:worker 冻在第一步、exe 没改写、队列永远挂着。
run-worker.ps1 现在启动做两件事:
0x01F7 → 0x0000):鼠标点击/拖选、键盘、Ctrl+C 都不再交给进程
(ENABLE_PROCESSED_INPUT 关掉,误按也不会打断);只动输入缓冲,输出与任务本身完全不受影响。GetSystemMenu + DeleteMenu(SC_CLOSE),实测菜单项 10 → 9、
GetMenuState(SC_CLOSE) 返回"不存在"):因此 × 与 Alt+F4 都点不动;最小化/最大化/移动保留(无害)。
真的需要中止时,用任务管理器结束那个 powershell.exe —— 这是刻意保留的唯一逃生口。
等应用退出的长等待型任务本来就用 -WindowStyle Hidden 无窗口运行。workerPid)。若某次启动发现"队列还在、而那个 worker 已经没了",
reconcileStalledPending() 就把任务交给重试路径,用 -WindowStyle Hidden 在后台重跑(实测:任务被成功补完)。jobId(= 入队时间)。worker 拿到 exe 锁后先核对队列是否仍是自己那一版,
否则打印 SUPERSEDED 并什么都不改就退出;pendingExplorer 也由 worker 在真正改写成功后设置,
不再由宿主在"刚拉起 worker"时就提前置位(那会导致刷了缓存但图标还没换)。pending 显示为"上次任务没有跑完"时,可以点「取消排队」,或直接再点一次应用(新请求会覆盖它)。只改 exe 而不刷缓存,图标文件是新的、shell 还在供旧位图(本机实测:exe 指纹、 三个快捷方式全对,桌面仍显示旧图标)。只重启 Explorer 也不够 —— 缓存库会活下来, 必须删库;而
AutoRestartShell=1会在杀掉 Explorer 后约 1 秒把它拉回、重新锁住文件, 所以删库必须"杀+抢删"带重试(refresh-shell.ps1已按此实现,实测 6/6 删除成功)。
排查入口只有一个:
$DSH_HOME/icon-changer/helper.log。宿主侧以[host]前缀、 启动器以[wmi-spawn]前缀、worker 以自己的时间戳写入,任何一环失败都能在这里看到。
装法就是"放到磁盘上 + 挂进 profile",仓库里没有安装脚本。先把代码取到本地:
git clone https://github.com/flycucu/dsh-icon-changer.git "$env:USERPROFILE\.dsh\plugin-src\dsh-icon-changer"
然后两处链接:
mklink /J "%USERPROFILE%\.dsh\profiles\desktop\node_modules\dsh-icon-changer" "<插件目录>"%USERPROFILE%\.dsh\profiles\desktop\package.json 里加依赖 "dsh-icon-changer": "link:<插件目录>",
并把 dsh-icon-changer 加进 dsh.profile.bundles(宿主半边靠这一条进 Loader,客户端半边靠它被 roster 扫到)改完 package.json 要重启 DeepSeek Harness;客户端半边只在启动扫描时进入启动图,
不重启不会出现设置卡片。改动前先备份 package.json(它属于启动关键配置)。
卸载:从 dsh.profile.bundles 与依赖里删掉,再删 junction。
打开 设置 → 应用图标:
assets/whalemaid.ico → 显示为「鲸鱼娘(预置)」,assets/original.ico → 显示为「原版」)
与已上传的图标并排显示,点一下选中;文案见宿主 BUNDLED_LABELS,改文案要重启应用才生效not-an-ico);
上传的图标可以配一个 <名字>-tray.ico 伴生文件(放 $DSH_HOME/icon-changer/icons/),托盘会优先用它×(悬停显示)。内置/原版没有 ×,
正在使用中的那个也没有;宿主侧同样拒绝(in-use / builtin-readonly),旧页面绕不过去每张卡片固定留出"类型词条"与"当前使用"两行的高度:上传的图标没有词条,但用等高空位占住, 免得它那张卡片比别的矮、整行看着变形。
卡片顶部常驻 「当前图标」,并且默认停在应用当前真正穿着的那个图标上
(宿主把 state.applied.icon 反查成图标 id 作为 appliedId 返回;用户手动点过别的图标后不再覆盖他的选择),
对应那张卡片还会挂一个绿色 当前使用 角标。卡片同时显示目标 exe 路径、排队中的任务、上次执行结果。
更换时弹出的那个 PowerShell 窗口:会依次显示
正在更换图标,请勿关闭此窗口 → 正在等待应用退出… → 正在写入新图标… → 图标已写入,正在重新启动应用… →
已完成,本窗口即将自动关闭,然后进程退出、窗口自己关掉(关掉窗口 = 杀掉任务,所以别手动关)。
等待用户自己退出应用的那类任务(restart:false / 启动补做)可能等很久,会用 -WindowStyle Hidden 静默运行,不弹窗。
http://127.0.0.1:<端口>/icon-changer(端口就是 Web GUI 那个,把路径换掉)。
页面上的按钮和设置卡片一一对应,UI 出问题时可以走这条。
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /api/icon-changer/status | exe / 备份 / 当前图标(applied + appliedId)/ 待处理任务 / 上次结果 |
| GET | /api/icon-changer/icons | 内置 + 已上传图标列表 |
| GET | /api/icon-changer/log?lines=200 | helper.log 尾部(宿主 / 启动器 / worker 统一日志) |
| GET | /api/icon-changer/preview/<id> | 图标原始字节,供 <img> 预览 |
| POST | /api/icon-changer/upload | {name, b64},最大 4 MiB |
| POST | /api/icon-changer/apply | {icon, restart, targets} |
| POST | /api/icon-changer/restore | {restart, targets} |
| DELETE | /api/icon-changer/icon/<id> | 删除上传的图标;内置/原版(builtin-readonly)与正在使用中的(in-use)会被拒 |
| POST | /api/icon-changer/clear | 清掉卡住的排队任务 |
图标 id 形如 builtin:whalemaid.ico 或 user:my_icon.ico。
所有写操作都过同源校验(Origin 与 Host 不一致直接 403)。
| 路径 | 内容 |
|---|---|
$DSH_HOME/icon-changer/state.json | 任务与结果状态 |
$DSH_HOME/icon-changer/icons/ | 你上传的图标 |
$DSH_HOME/icon-changer/backup/DeepSeek Harness.exe.orig | 原始 exe 备份(首次更换时创建,只创建一次) |
$DSH_HOME/icon-changer/helper.log | 宿主 / 启动器 / worker 的统一日志 |
$DSH_HOME/icon-changer/pending-job.json | 交给 worker 的任务描述(宿主写、worker 读) |
$DSH_HOME/icon-changer/pending-job.cmdline | 交给 WMI 创建的命令行(避免多层引号地狱) |
$DSH_HOME/icon-changer/spawn-result.json | WMI 创建结果(无 BOM,供宿主判成败) |
手工回滚(插件用不了时的兜底):
Copy-Item "$env:USERPROFILE\.dsh\icon-changer\backup\DeepSeek Harness.exe.orig" `
"<你的安装目录>\DeepSeek Harness.exe" -Force
备份是在首次更换之前做的,所以它始终是官方原版,多次换图标也不会被覆盖成中间态。
icon-changer/
package.json # dsh.bundle.patch 指向 cordis.patch.yml;dsh.client 声明浏览器半边
cordis.patch.yml # 往 profile 插一行宿主插件
lib/index.js # 宿主半区:路由、状态、任务落盘、用 WMI 拉起 worker
lib/client.js # 客户端半区:设置页卡片(__ModuleLoader__ + React,无构建步骤)
lib/ui.html # 独立管理页面(备用,原生 JS)
bin/wmi-spawn.ps1 # 启动器:读命令行文件 → Win32_Process.Create(逃出应用 job object)
bin/run-worker.ps1 # bootstrap:读 pending-job.json → 调 apply-icon.ps1
bin/apply-icon.ps1 # worker:等待解锁 → rcedit → 校验 → 记状态 → 可选重启
bin/refresh-shell.ps1 # 刷 shell 图标缓存:ie4uinit → 杀 Explorer → 抢删 iconcache → 起 Explorer → 广播
bin/console-messages.ps1 # 给 worker 窗口取中文提示(dot-source 共用)
bin/messages.txt # 中文提示文案(UTF-8 数据文件;脚本本体保持 ASCII-only,避免 PS 5.1 无 BOM 乱码)
tools/rcedit-x64.exe # rcedit 2.0.0(Electron 官方维护的 PE 资源编辑工具)
assets/whalemaid.ico # 内置图标
_test_plugin.mjs # 自测:mock cordis 上下文,真跑 /apply 全链路
自测(用一份改名的 node.exe 副本当"应用 exe",DSH_HOME 指向临时目录,
所以只会改那份副本;别传 restart:true,那条路会 taskkill 真的应用):
$t = "$env:TEMP\ic-test"; New-Item -ItemType Directory -Force $t | Out-Null
Copy-Item (Get-Command node).Source "$t\DeepSeek Harness.exe"
$env:DSH_HOME = "$t\home"
& "$t\DeepSeek Harness.exe" .\_test_plugin.mjs
Get-Content "$t\home\icon-changer\helper.log" # 等几秒,看 RESULT ok=True
# 第二阶段:证明宿主能把 worker 写的 state 读回来(BOM 回归守卫)
& "$t\DeepSeek Harness.exe" .\_test_plugin.mjs --status-only
只做语法检查:npm run check。
ctx.webServer 上、只监听 127.0.0.1,不带 Web GUI 的 token:本机任何进程都能调 /apply。
跨源浏览器页面会被 sameOrigin() 挡掉(Origin 与 Host 不一致 → 403),所以拦住的是网页,不是本机程序。
换句话说:同用户、同机器即受信,与"能改本机 exe 的桌面插件"这一前提一致。若你的机器上有不受信的本机程序,请自行取舍。basename + 非 \w 压成 _),预览/删除都只认数据目录里的图标 id,
不接受任意路径。builtin-readonly / in-use),即使绕过界面直接调接口也一样。本插件为 MIT(见 LICENSE)。随仓库分发的第三方组件与内置图标说明见
THIRD_PARTY_NOTICES.md:tools/rcedit-x64.exe 来自
electron/rcedit(MIT, Copyright (c) 2013 GitHub, Inc.);
assets/whalemaid*.ico 为作者自有美术资源,assets/original*.ico 是官方原版应用图标(仅为方便回退而收录)。
npx dsh web 跑的(process.execPath 是 node.exe),/apply 返回 not-desktop-app。lib/ui.html 只做"看状态 + 换图标/回退 + 看日志",删除图标请走设置页卡片。