deepseek-harness
deepseek-ai
DeepSeek Harness: Everything is a Plugin.
PROJECT TOPICS
INSTALL REFERENCE
dsh plugin --profile web add github:jifeng15/dsh-web-restart
该命令指向仓库当前默认分支;尚无绑定当前 commit 的完整验证结果。
PROJECT README
让 dsh web 实现"真·热装载":装插件、改配置、升级本体后,不用再手动跑去命令行重启,实时看到效果。
简体中文 | English
我在使用 dsh web 的过程中发现:DSH 有三类变更必须重启进程才能生效——装/卸/更新插件(bundle 层启动时合成)、修改 profile 配置(cordis.patch.yml)、升级 dsh 本体。
但每次遇到这些情况,我都得离开对话窗口、跑去命令行重新输入 dsh web,然后刷新页面才能看到效果。整个过程:
这个 skill 把"热重载"和"热重启"同步起来——让 DSH 该热重载的继续热重载(skill、AGENTS.md、settings 本来就是),该重启的由 agent 自动安全地重启,让 dsh web 实现真正的热装载:你只管在对话里装插件,刷新一下就能实时看到效果,不用再手动去重启。
三种安装方式,任选其一:
① 对话安装(推荐)——在 DSH 对话里说(记得附上仓库地址,agent 才能知道去哪安装):
agent 会自动完成安装并加载本 skill(skill 是热加载的,装完即用,无需重启)。
② 一行命令安装——在自己的终端执行:
npx -y skills add https://github.com/jifeng15/dsh-web-restart -g -y -a universal --copy
npx skills add只装 skill 文件(agent 用 skill 内的脚本路径,不需要命令)。 想要终端里能用dsh-web命令,再执行一次:bash ~/.agents/skills/dsh-web-restart/install.sh --bin-only(装到~/bin/dsh-web并自动把~/bin加进 shell PATH)。
③ clone 手动安装——想先看源码/自己维护时:
git clone https://github.com/jifeng15/dsh-web-restart.git && cd dsh-web-restart
bash install.sh
装完即可用(见下方"两种使用场景")。
你只需要安装了本 skill 后,在对话里说"帮我装 XX 插件"(XX = 任意其他 插件,如 dsh-market)、"帮我升级 dsh",agent 会:
dsh-web install(热装免重启);升级本体走 dsh-web upgrade(先升级再安全重启)适用:日常通过 DSH 对话操作插件的用户。这是 skill 的主要使用场景——它本来就设计给 agent 用的。
首次使用说明:第一次热装插件时,agent 会自动安装热装组件并重启一次 (让组件生效)——你只需刷新那一次。之后每次装插件都是免重启的。
如果你习惯自己开终端操作,装/卸插件用热装命令(免重启),其他场景用统一重启:
dsh-web install <spec> # 装插件:热装优先(免重启),失败回退安全重启
dsh-web remove <pkg> # 卸插件:热卸优先(免重启),失败回退安全重启(拒绝 CLI 管理的插件——请用 `dsh plugin --profile web remove`)
dsh-web restart # 改 profile 配置、升级本体等场景,统一安全重启
dsh-web session # 随时查看实际 tmux 会话名(自动发现)
dsh-web status # 随时查看状态
热装优先,重启兜底:装/卸插件默认走内置 dsh-web-hot 热装——运行中应用 patch, 不用重启、PID 不变;热装不可用(插件未加载 / 模块代码级更新 / 无法热应用)时 自动回退安全重启。
为什么"多一步"省不掉:skill 的"自动重启"触发器是 agent——你通过对话装插件时 agent 在场,能自动调起;但你自己开终端装插件时没有 agent 参与,所以必须手动跑一次。 这"多一步"是一次性的:它会把 dsh web 迁入 tmux 托管,之后任何变更(对话方式) 就全自动了,不用再管。
适用:命令行用户、脚本自动化、以及 agent 内部调用。所有命令都自动处理 tmux 托管、端口发现、会话发现,不需要你先做任何准备。
| ✅ 能 | ❌ 不能 |
|---|---|
| 热装/热卸插件(内置 dsh-web-hot)——免重启(不可用时自动回退安全重启) | agent 重启后无缝继续对话——重启会短暂断开,需你刷新页面(结构性限制) |
| 装完插件后自动重启 dsh web,你不用手动敲任何命令(对话方式) | 模块代码级更新无法热应用(Node require 缓存,必须重启) |
改完 profile 配置(cordis.patch.yml)后 reload 生效 |
让 dsh web 不重启进程就加载插件/配置(bundle 树启动时合成,必须重启进程;刷新页面只是重启后重连,不是加载手段) |
upgrade 升级 dsh 本体并自动重启 |
重启电脑后自动恢复(需要你重新 dsh-web start 一次) |
| 没装 tmux?自动帮你装(macOS/Linux 主流包管理器) | 非 npm/pnpm 安装的 dsh 本体无法自动升级(只提示) |
dsh web 跑在普通终端?自动迁入 tmux 托管(运行中执行一次 dsh-web start/restart;或启用看门狗后完全自动) |
skill 增改、AGENTS.md 修改等(这些本来就是热加载的,不需要它) |
会话名不叫 dsh-web?自动找到实际托管会话 |
崩溃自动重启是独立可选功能(run-loop) |
端口不是 3080?自动发现(含 --port 0 随机端口) |
— |
| 终端窗口全关,dsh web 照常运行、随时可重启 | — |
一句话:装/卸插件免重启(热装),真正必须重启的(改配置、升级本体、迁移)安全自动重启并让 dsh web 常驻;你只需要在页面断开后刷新一下。
dsh-web install <spec> # ★ 装插件:热装优先(免重启),失败回退安全重启
dsh-web remove <pkg> # ★ 卸插件:热卸优先(免重启),失败回退安全重启(拒绝 CLI 管理的插件——请用 `dsh plugin --profile web remove`)
dsh-web restart # ★ 兜底/其他:装插件后、改配置、升级本体等场景统一安全重启
dsh-web reload # 改完 profile 配置(cordis.patch.yml)后重启生效(= restart)
dsh-web upgrade # 升级 dsh 本体并自动重启
dsh-web start # 启动/自动接管(无会话则创建,未托管则迁入 tmux)
dsh-web stop # 停止(Ctrl-C;tmux 托管保留,`restart` 可快速恢复)
dsh-web quit # 彻底退出:停止 + 关闭托管会话 + 清理痕迹(不再挂后台)
dsh-web heal # 修复裸进程托管(run-loop 丢失时恢复规范托管;= 一次安全重启,会断一次)
dsh-web status # 查看会话/端口/PID/日志
dsh-web session # 查看实际 tmux 会话名(自动发现,不假设 dsh-web)
dsh-web attach # 进入 tmux 排查
dsh-web autostart-on # 启用开机自启(默认关闭,用户主动选择;launchd/systemd)
dsh-web autostart-off # 关闭开机自启
dsh-web autostart-status # 查看自启状态
dsh-web watchdog-on # 启用 launchd 看门狗:每 30s 把未托管的 dsh web 自动迁入 tmux(默认关闭)
dsh-web watchdog-off # 关闭看门狗
dsh-web watchdog-status # 查看看门狗状态
dsh-web repair # 修复配置:同文件重复 id / ghost bundle / 跨来源重复(改前自动备份)
dsh-web health-check # 检查端口 + 配置树——是进程问题还是配置问题?
dsh-web preflight # boot 前自检(start/restart 自动执行):清跨来源重复
# 内部命令:report-port(写实际端口到 last-port.txt + 非默认端口通知)、watchdog-tick(launchd 调用)
热装优先,重启兜底:
dsh-web install/remove先走内置 dsh-web-hot 热装—— 免重启;热装不可用(插件未加载 / 变更无法热应用)时自动回退安全重启。dsh-web restart仍是其他场景(改配置、升级本体、迁移)的统一命令。每个插件只有一个主人(单源原则):插件只从一个来源挂载——要么
dsh plugin --profile web add|remove(dsh.profile.bundles列表),要么热装 (cordis.patch.ymlpatch 层),不能两边都有。若同一插件出现在两边(如外部dsh plugin add收编了之前热装的插件),preflight(start/restart自动 执行)与repair会自动检测并移除 patch 层重复行——下次启动不会再因duplicate loader entry id崩溃。卸载侧同样:dsh-web remove拒绝 CLI 管理的 插件(会留 ghost bundle),引导走dsh plugin remove。
开机自启是可选功能,默认关闭——skill 不会自动启用任何自启项。需要开机后 dsh web 自动在 tmux 里起来时,用户主动执行
dsh-web autostart-on即可。
端口通知策略:
start/restart后实际端口写入~/.dsh/logs/last-port.txt。 端口为默认值(3080)时不发通知(大家都知道);非默认端口(被占用/随机) 才发系统通知告知实际端口——尤其自启是无人值守场景,用户需要知道连哪里。
dsh-web watchdog-on 安装 launchd LaunchAgent,每 30s 检测一次:如果 dsh web
正在监听但不在 tmux 托管(如你在普通终端里起的),就自动把它迁入 tmux——
之后不管你怎么启动的,关终端都不会杀掉 dsh web。看门狗只迁移运行中的
web,绝不主动启动停止的 web。dsh-web watchdog-off 关闭。当前仅 macOS
(Linux systemd timer 后续支持)。
| 项 | 默认值 | 覆盖 |
|---|---|---|
| tmux 会话名 | dsh-web(找不到时自动发现) |
DSH_WEB_SESSION |
| 端口 | 自动发现(显式配置 > 进程命令行 --port > 进程监听端口 > node 扫描 > 默认 3080) |
DSH_WEB_PORT |
| 启动命令 | dsh web |
DSH_CMD |
--port 8080 或 --port 0(随机端口)启动 dsh web 也能正确工作——脚本会从进程命令行或实际监听端口自动解析,无需手动指定。tmux kill-server 后:用你习惯的方式重新打开 dsh web 即可——
dsh-web start 最省事(一步建 tmux + 启动 + 托管);直接 dsh web 也可以,
第一次 restart 会自动迁入 tmux(多一次自动迁移,之后全自动)。tmux run-shell -b "sleep 3; tmux send-keys -t dsh-web C-c; sleep 2; tmux send-keys -t dsh-web 'dsh web' Enter"
tmux server 是独立守护进程,不依赖 dsh web 存活——即使调用方(agent)随 dsh web 被杀,重启依然完成。
| 组件 | 职责 |
|---|---|
scripts/dsh-web.sh |
主命令行(start/restart/install/remove/upgrade/status/session/autostart-*) |
hot-plugin/(dsh-web-hot) |
宿主插件:include.update 热装/热卸(免重启) |
scripts/run-loop.sh |
崩溃自动重启循环(3 次熔断) |
scripts/install-tmux.sh |
跨平台 tmux 自动安装 |
install.sh |
一键安装(skill + 命令行 + hot-plugin + tmux) |
dsh-web.sh install <spec>
→ POST /dsh-web-hot/install {spec}
→ pnpm add <spec>(profile 目录,官方 registry)
→ 读 bundle 的 dsh.bundle.patch → patch 行
→ 写 cordis.patch.yml(用户补丁层,持久化)
→ include.update(热应用,PID 不变)
→ 记录 dsh-web-hot.state.json
→ {"ok": true}
dsh-web.sh restart
→ resolve_session(发现实际会话,如 "0")
→ tmux run-shell -b "sleep 3; C-c; sleep 2; 'dsh web'"
→ tmux server 独立执行(即使 agent 被杀也完成)
→ 5-8 秒后 dsh web 重启;用户刷新
dsh plugin --profile web add(bundle 列表)与热装(用户 patch 层)都往
同一个 loader 里注入 include 条目,而 entry id 必须全局唯一——同一插件出现在
两个来源,下次启动就报 duplicate loader entry id。本项目强制每个插件只有一个
主人:
dsh.profile.bundles 中的插件(安装时点守卫);preflight(start/restart 自动执行)与 repair 检测跨来源重复——
bundles 声明的 entry id ∩ patch 层行 → 移除 patch 层重复行(bundle 侧为权威)
并同步 dsh-web-hot.state.json;dsh-web-hot 插件启动自愈——覆盖运行期漂移(HMR 重载、手改 state),经
include.update 热卸载让位。所以外部 dsh plugin add 收编了之前热装的插件时,下次启动前会自动对齐——
不再需要手动清理。
| 依赖 | 用途 | 缺失时 |
|---|---|---|
| tmux | 托管 + 独立重启 | 自动安装 |
| pnpm | 插件安装(经 dsh-web-hot) | 热装降级为安全重启 |
| dsh CLI | install.sh 装 hot-plugin | 跳过 hot-plugin |
| curl/lsof/ps/pgrep | 探测 | — |
tmux run-shell -bnohup ... & 排定后台任务会被调用方回合清理 → 用 tmux servergithub:owner/repo#refdsh web(双启动) → 新实例抢端口失败(EADDRINUSE),
旧实例可能被挂起(run-loop 收到信号退出)。别双启动:用 dsh-web start/
restart/status。误敲了也别慌:看门狗 30s 内自动恢复被暂停的实例,
dsh-web restart 一键恢复规范托管。
⚠️ 跑 restart 前请知悉:重启会短暂断开 web、中断正在其中运行的
agent 会话——等方便时再跑,或让下一个会话帮你执行。跑完用
dsh-web status 验证 run-loop 出现在父进程链里(说明崩溃自动重启已恢复,
否则只是裸进程托管)。裸进程托管不用手动管:看门狗会自动检测并在 30s 内
修复回 run-loop 托管(也会断一次);想立即修就 dsh-web heal。pnpm 11 的 24 小时 minimumReleaseAge 供应链闸门:锁文件里出现当天刚发布
的插件时,dsh plugin / dsh-market 等工具的更新可能警告或失败("too-young
release")。本项目的热装按次用 --config.minimumReleaseAge=0 绕过,不受影响;
dsh-market 会自带一次重试,但可能仍受镜像同步滞后影响(本机 ~/.npmrc 指向
npmmirror,dsh-market 未固定官方 registry)。
想彻底放行(例如你经常装当天新发布的插件),可在
~/.dsh/profiles/web/pnpm-workspace.yaml 加:
minimumReleaseAge: 0
代价:放弃 24h 供应链闸门(防"刚发布即被滥用"的包)。按需取舍,改完
dsh-web reload 生效。
MIT
~/bin/dsh-web 启动时托管曾退化为「无 run-loop 裸进程」
(install.sh 早期没装 run-loop.sh),看门狗误判裸托管并误杀 → 第一次 start
端口起来后整个 tmux server 消失("全灭"),第二次因 5 分钟冷却侥幸存活。crash_loop_cmd 现在会回退到 skill 副本 scripts/ 找 run-loop.sh;install.sh 同时安装 run-loop.sh 到 bin 目录;auto_rehost 增加"托管会话已不存在则跳过"保险。dsh-web heal:手动触发同一修复(无冷却限制)。dsh web,新实例抢端口失败(EADDRINUSE)、旧实例可能被挂起——
看门狗 30s 内把它恢复。恢复指引:dsh-web restart 重建规范托管。dsh-web quit:彻底退出——停止 dsh web(C-c,兜底 TERM)+关闭 tmux
托管会话+清理痕迹(crash-count),系统里不再挂任何 dsh web 相关进程/会话。
stop 只发 Ctrl-C(托管保留,restart 快速恢复);quit 是"不干了"的出口。
看门狗在运行时会提示(它只迁移运行中的 web,不会自动重启已停止的)。ensureWorkspaceAllowed 现在合并进 dsh-market 写的 allowBuilds
对象风格(name→boolean,含它的 "set this to true or false" 模板),不再用
我们的旧列表格式覆盖它。两个工具写同一个文件不再互相清掉。install.sh 安装 dsh-web 命令并自动把 bin 目录加进 shell PATH
(此前命令装了却找不到)。dsh-web watchdog-on:启用 launchd 看门狗(默认关闭,用户主动开启)。
每 30s 检测一次——端口有 dsh web 但不在 tmux 托管(如普通终端里起的)→
自动迁入 tmux。任何方式启动的 dsh web,关终端都不再影响。看门狗只迁移
运行中的 web,绝不主动启动停止的 web;用 watchdog-status / watchdog-off
管理。start 自动接管(普通终端里的 dsh web → tmux)现在先建空 tmux 会话,
等旧进程停止、端口释放后才在托管会话里拉起。原来新实例立即启动、和旧进程抢
端口(EADDRINUSE),失败会被 run-loop 计入 3 次熔断。dsh-web remove <pkg> 现在拒绝由 dsh plugin 管理的插件(在
dsh.profile.bundles 中)并引导走 dsh plugin --profile web remove <pkg>。
旧回退路径只删依赖、不删 bundle 条目(ghost bundle),现在从源头挡住。
卸载路径同样贯彻「每个插件只有一个主人」。dsh.profile.bundles 又被热装写进 patch 层)现在会被预防并自动修复:dsh-web repair 新增诊断 0:bundles 声明的 entry id ∩ patch 层行 → 移除
patch 层重复行(bundle 侧为权威)并同步 dsh-web-hot.state.json。dsh-web preflight(boot 前自检)——start/restart 拉起前自动执行;
外部把已热装插件收编进 bundles(如 dsh plugin add)后,下次启动不会再崩。dsh-web-hot 启动自愈——state.json 中记录的热装包若已被
dsh.profile.bundles 收编 → 自动让位(删 patch 行 + 清 state +
include.update 热卸载),覆盖运行期漂移 / HMR 重载。repair / health-check / 自动备份(当时 README 未同步)。dsh-web repair:同文件重复 id 去重、ghost bundle 移除、state.json 与
patch 层对齐;改配置前自动备份(保留最近 10 份)。dsh-web health-check:端口 + 配置树(--dump-config)双检查——区分
「进程问题」还是「配置问题」。dsh.profile.bundles 中的插件(热装侧防重复挂载)。dsh-web-hot 宿主插件,
通过 include.update 在运行中热应用 patch 行,PID 全程不变。dsh-web install <spec>(热装优先,失败回退安全重启)、
dsh-web remove <pkg>(热卸优先,失败回退安全重启)。dsh-web session:随时查出实际 tmux 会话名(自动发现,不假设 dsh-web)。CLASSIFICATION EVIDENCE
系统优先读取 GitHub Topics,再与站内分类词典和词根规则比对。当前命中: dsh-skill。