dsh-launch:把长驻服务交给独立 broker 进程监督

前言

在 DSH 对话里让模型启动一个 dev server,常遇到的问题是:对话回合一结束、会话一关闭,服务就没了。根因在于 pwsh 的后台任务(jobs)绑定在 owner 会话作用域上,会话或回合结束时,dsh-jobs-local 会取消归属任务——服务根本活不到下一轮。

dsh-launch 针对的就是这个问题:把服务交给一个独立的 detached broker 进程监督,使服务在对话回合结束、会话关闭、甚至 DSH 自身重启后继续运行。下面介绍它的机制、功能与用法。

这是什么

dsh-launch 是 Khellendros97 维护的 DSH 服务管理插件,MIT 许可,当前版本 0.2.0,要求 Node >= 20。监督机制参考了 omp 的 launch daemon broker。

核心思路分三步:

1、服务由 detached broker 进程以 detached + stdio 落盘 的方式拉起,stdio 直接写入磁盘日志;
2、broker 只做监督——日志增量、pid 探活、就绪检测、崩溃退避重启——自身空闲即退;
3、DSH 下次启动时重新拉起 broker,并按 pid 收养仍在运行的服务。

插件是单包发布:Service 侧边栏 tab、broker、模型工具全部在同一包内;better-sidebar 只负责承载 tab(经其扩展 API 注入,better-sidebar 侧无需任何改动)。

核心功能

模型工具

  • service_start:启动长驻服务,必填 name + command,可选 cwd/title/logo/env/readyLog/readyPort/restart,等待就绪后返回;
  • service_stop / service_restart / service_list / service_logs:停止(整树杀)/ 重启 / 列举 / 读日志尾。

就绪判定会先剥离 ANSI 转义,再对端口做 IPv4/IPv6 双栈探测。

Service tab(侧边栏)

侧边栏中以服务卡片展示标题、logo(可选,URL 或图片路径)、状态徽标、PID、运行时长、重启次数,支持启动 / 停止 / 重启 / 查看日志。tab 可见时轮询插件自有的 fenced /launch/api 路由。

没有安装 better-sidebar 时,插件以 headless 模式运行:工具可用,无 UI tab。

ctx.launch(cordis 服务)

供其他插件消费,提供 ping / list / start / stop / restart / describe / logs

监督机制

每服务维护一个状态机:starting→running→ready→stopping→exited/failed(开启重启且失败时进入 restarting 退避)。重启策略有 no / on-failure(默认)/ always 三种,退避为指数递增,从 1s 到 30s。

通信上,宿主与 broker 之间采用命名管道 NDJSON 协议 + token 认证,管道名按 runtimeDir 哈希,多部署不会冲突。

就绪检测有两个开关:

  • readyLog:输出正则匹配,ANSI 转义已剥离;
  • readyPort:TCP 探测,未指定 host 时同时探 127.0.0.1::1

start/restart 等待就绪默认 20s,上限 60s。broker 重启后收养 starting 记录时,会重扫日志尾部已滚过的就绪标记,避免误判。

Windows 上,command 字符串服务经 cmd.exe /d /c 由内置 node 启动器(broker/launcher.cjs)执行——detached 的 cmd 会丢失批处理子进程输出,启动器规避了该问题;整树停止用 taskkill /T

安装与启用

前置条件:DSH web 环境;dsh-better-sidebar 是可选依赖(^0.11.0 || ^0.12.0),仅 UI 需要。

dsh plugin --profile web add dsh-launch

一条命令完成安装 + 自动挂载:包内 cordis.patch.yml 会注册进 dsh.profile.bundles。装完先重启 dsh web,再硬刷新浏览器(Cmd/Ctrl+Shift+R)。

配置

插件行可选 config,共有三个配置项:

- id: dsh-launch
  name: dsh-launch
  config:
    runtimeDir: '~/.dsh/tmp/dsh-launch'   # 运行时目录,默认同左
    idleGraceMs: 15000                     # 无客户端/无 presence 后的退出宽限
    maxLogBytes: 8388608                   # 单服务日志轮转阈值,8MB

运行时目录 ~/.dsh/tmp/dsh-launch/ 下包含:broker.tokenbroker.pid(单实例租约)、presence/*.json(DSH 存活标记)、daemons/<name>/(元数据 + 日志,8MB 轮转)。

测试

npm test

测试分两部分:broker 冒烟 15 项(启动、就绪含 ANSI 与 IPv6、日志、停止、重启、持久化、杀 broker 后收养、收养尾部重扫)与接线 13 项(服务提供 / 工具注册 / presence / 真实 broker 往返 / fenced 路由信封 / 围栏拒绝 / 客户端契约)。

安全说明

  • 服务以运行 DSH 的同一用户身份、脱离任何沙箱执行——这是服务存活的前提,服务等同于用户在终端启动的进程;
  • /launch/api/launch/service-logo 全部经 Host 头信任围栏(loopback 或 trustedHosts);broker 管道仅绑定本机(命名管道 + token);
  • 插件卸载(HMR/禁用)不会停止服务或 broker;broker 在无 presence 且空闲 15s 后自行退出,服务继续运行。

限制

  • 服务名全局唯一(全机一个 broker 作用域);
  • 无 PTY / 无 stdin:交互式进程请改用 better-sidebar 终端;
  • broker 死亡窗口内(DSH 未运行且 broker 已退出)崩溃的服务不会自动重启,detached 服务本身不受影响。

适用场景与注意

适合的场景:dev server、watcher、mock API、worker 这类需要跨回合、跨会话存活的进程。只要流程里有「模型起了个服务,下一轮还要用」,这个插件就把服务生命周期从对话生命周期里解耦出来。

注意两点:插件以当前 dsh 进程权限运行,服务也以同一用户身份、脱离沙箱执行;安装前建议自行检查源码与许可证(MIT)。

结尾

回顾一下:dsh-launch 用一个 detached broker 进程解决后台服务随会话回收的问题,服务在回合结束、会话关闭、DSH 重启后都能继续运行,同时提供模型工具、侧边栏 UI 和跨插件 API 三套入口。DSH 的理念是「一切皆插件」,这类把基础设施问题封装成插件的思路,值得在搭建自己的工作流时参考。

  • GitHub:https://github.com/Khellendros97/dsh-launch
  • 社区目录页:https://www.skillhub.cn/plugins/Khellendros97/dsh-launch (社区站点,与 DeepSeek / 幻方无官方从属关系)
羽毛球分组比赛记分
小程序二维码

欢迎使用《羽毛球分组比赛记分》微信小程序

Xiaoye