Back to home@QuanQQQ

dsh-plugin-dev-manager

Stable control plane for isolated DeepSeek Harness plugin development

Stars
0
Language
TypeScript
Created
Aug 24, 2026
Updated
Aug 26, 2026
GitHub repo

Introduction

dsh-plugin-dev-manager

在一个稳定的 DeepSeek Harness 中管理隔离的 Plugin 开发实例。

它解决这样的开发风险:目标 Plugin 的 Host 代码、配置或依赖发生错误时,开发 DSH 可以重启或启动失败,负责写代码的稳定 DSH 仍然保持运行。

稳定 DSH
  └─ dsh-plugin-dev-manager
       ├─ Settings / Plugin Dev
       └─ 本地 supervisor
            ├─ Workspace A → Plugin A + Common + Auth → 127.0.0.1 / LAN:3081
            └─ Workspace B → Plugin B + Storage → 127.0.0.1 / LAN:3082

supervisor 作为独立 Node.js 进程运行。稳定 DSH 重启或重新加载 Manager Plugin 后,会通过带随机令牌的 localhost RPC 重新连接到原 supervisor。

从旧版本升级时,Manager 会把旧 supervisor 返回的单 Plugin 数据自动补成单成员 Workspace,因此原有项目可以继续展示和使用。多 Plugin API 需要 supervisor protocol v2,实例删除 API、可恢复删除记录和注销后保留的数据所有权需要 protocol/registry v3。若当前 supervisor 版本过旧,先保存开发实例中的工作并执行 dsh_dev_shutdown({ confirm: true });下一次 Manager 调用会启动新版 supervisor 并迁移 registry。该操作会停止旧 supervisor 管理的开发实例。registry v3 是前向升级,旧版 supervisor 不再读取它。

当前能力

  • 为每个开发 Workspace 创建独立 DSH_HOME、Web profile 和端口。
  • 一个 Workspace 可以组合多个本地 Plugin、固定版本依赖和团队 Preset。
  • 为开发实例设置独立 DSH_AGENTS_HOME 和 bundled-skill 目录,避免扫描稳定实例的 Skills。
  • 执行依赖安装与初始构建。
  • 通过 link:<absolute-plugin-path> 安装所有本地成员,并为每个成员独立运行 build/watch。
  • 支持仓库内的 .dsh-dev.yml.dsh-dev.yaml.dsh-dev.json 声明。
  • 在启动前运行 dsh --profile web --dump-config
  • 启动、停止、重启、检查一个开发 DSH。
  • 安全注销单个实例,并可选择清理受管开发数据,同时保留已晋级 Release 和 Plugin 源码。
  • 自动启动每个本地成员的 dev:client watcher(存在该 script 时)。
  • localhost supervisor RPC 使用原生 HTTP,绕过 Devbox 的环境代理。
  • supervisor 启动失败或竞争失败时主动回收刚创建的进程。
  • 在 DSH 设置页提供 Plugin Dev 管理面板。
  • 读取 Host、watcher、check、clean validation 和 supervisor 日志。
  • 执行目标包的 checktest 或指定 package script。
  • 将 pnpm tarball 安装到全新的验证环境,完成 config composition 和 Web 健康检查。
  • 通过干净环境门禁后,复制原始 tarball、计算 SHA-256 并写入晋级元数据。
  • 拒绝停止 supervisor 没有持有进程句柄的端口占用者。

安装

直接使用 GitHub package spec 安装固定版本:

dsh plugin --profile web add \
  "github:QuanQQQ/dsh-plugin-dev-manager#v0.4.1"

仓库提交包含构建后的 lib/,因此目标机器无需安装源码构建依赖。私有仓库需要提前配置 GitHub SSH 访问;也可以使用完整地址:

dsh plugin --profile web add \
  "git+ssh://git@github.com/QuanQQQ/dsh-plugin-dev-manager.git#v0.4.1"

需要 tarball 时,可以从 GitHub Release 下载已验证的安装包:

gh release download v0.4.1 \
  --repo QuanQQQ/dsh-plugin-dev-manager \
  --pattern 'dsh-plugin-dev-manager-*.tgz'

dsh plugin --profile web add ./dsh-plugin-dev-manager-0.4.1.tgz

私有仓库需要先执行 gh auth login。也可以在 GitHub Release 页面下载 tarball,再复制到目标机器。

首次部署 PDM 时,推荐把发布 tarball 安装到稳定 DSH,让控制面与正在编辑的源码完全分离:

pnpm install
pnpm run check
pnpm pack --pack-destination ..

dsh plugin --profile web add /absolute/path/to/dsh-plugin-dev-manager-0.4.1.tgz

首次安装尚无 PDM 队列可用,应先确认稳定 Host 没有活动会话或后台任务,再由操作者按宿主环境的正常方式重启以加载 bundle。PDM v0.4.0 及后续版本之间的更新不得重复这条直装/直启流程,必须使用 dsh_dev_queue_update(confirm=true)。从不具备更新队列的 v0.3.x 或更早版本升级到 v0.4.0 时,需先保存所有工作、确认稳定 Host 已空闲并停止旧开发 supervisor,再将这次升级作为显式的一次性 fallback;目标 Plugin 不要安装到稳定 profile。

只在调试 Manager 本身的早期阶段使用 link 安装:

dsh plugin --profile web add "link:/absolute/path/to/dsh-plugin-dev-manager"

开发 Manager 本身时,可在控制面安装上一版稳定 tarball,再把当前源码仓库作为目标 Plugin 注册到 Manager。

启动稳定 DSH 后,打开 Settings → Plugin Dev。输入目标 Plugin 的绝对路径即可创建隔离环境。Agent 工具与 Web 面板操作同一个 supervisor 和项目注册表。

推荐开发 SOP

  1. 准备一个稳定 DSH,只安装 Manager 和日常开发所需的稳定插件。
  2. 在 Plugin Dev 面板或通过 dsh_dev_workspace_create 注册 Workspace。Manager 会读取开发清单、安装依赖、初始构建、分配独立端口,并把所有成员组合到独立 DSH_HOME
  3. 调用启动。Client 代码由目标包的 dev:client watcher 持续构建,DSH Client HMR 会加载新的 client.js
  4. 修改 Host 代码后,先保存工作,再执行受控重启。Web 面板会弹出确认,Agent 工具要求 confirm=true
  5. 调用检查。准备交付时执行“验证并晋级”或 dsh_dev_promote
  6. 使用 releases/latest.json 指向的原始 tarball 进行安装或发布,保留其中的 SHA-256 供核对。

这套流程把开发实例的故障域限制在单个 Workspace。目标 Host 启动失败或自行退出时,稳定 DSH、Agent 对话和其他开发实例仍可继续工作。

多 Plugin Workspace

在 Workspace 根目录添加 .dsh-dev.yml

version: 1
name: campaign-dev
preset: team-web

plugins:
  - path: .
    role: primary
    watch: dev:client

  - path: ../dsh-plugin-common
    role: member

  - source: github:example/dsh-plugin-storage#v1.3.0
    role: dependency

然后在面板输入 Workspace 路径,或调用:

dsh_dev_workspace_create({ workspacePath: "/work/campaign-plugin" })

primary 是执行 check、pack、promote 的主 Plugin;member 是一起构建和调试的本地 Plugin;dependency 是安装到同一 profile 的运行依赖。省略 role 时,第一个本地 Plugin 会成为 primary,其他本地 Plugin 使用 member,远程 source 使用 dependency。

安装顺序固定为 Preset、非 primary 成员、primary。这样 primary 的配置补丁最后参与组合。每个本地成员拥有独立 watcher PID 和日志。任意 Host 代码变化都需要重启整个 Workspace,Workspace 内的运行中任务会随之中断。

单 Plugin 场景可以继续使用 dsh_dev_create;它会创建只有一个 primary 成员的 Workspace。

Manager 以解析后的真实 workspacePath 作为共享实例边界:不同会话即使传入不同 id,只要 Workspace 与 Plugin 组成一致,就直接返回已有实例,不会重复安装、分配端口或启动 Host。若同一 Workspace 请求了不同组成,调用会提示刷新已有共享实例,而不是并行创建另一份。

配置

默认只允许管理稳定 DSH 启动目录下的 Plugin。需要管理其他目录时,在稳定 profile 的 cordis.patch.yml 覆盖配置:

- id: dsh-plugin-dev-manager
  config:
    allowedRoots:
      - /absolute/path/to/plugin-workspaces
    portStart: 3081
    portEnd: 3180
    startupTimeoutMs: 30000
    shutdownTimeoutMs: 7000
    usePollingWatch: false
    allowRemoteWebApi: false
    discoverLanUrls: true
    defaultPreset: team-web
    presets:
      team-web:
        - github:example/dsh-plugin-auth#v2.1.0
        - github:example/dsh-plugin-observability#v1.4.2

可用字段:

字段默认值说明
stateDir$DSH_HOME/dev-managersupervisor、项目注册表、实例 home、日志和产物目录
allowedRoots稳定 DSH 的启动目录可管理 Plugin 的路径白名单
dshCommanddsh开发实例使用的 DSH 可执行文件
packageManagerpnpm安装、构建、检查和打包命令
portStart / portEnd3081 / 3180自动分配端口范围
startupTimeoutMs30000等待开发 DSH 监听端口的时间
shutdownTimeoutMs7000SIGTERM 后等待时间,超时升级到 SIGKILL
operationTimeoutMs600000install、build、check、pack 的最长时间
maxOutputBytes131072单次命令和日志返回上限
usePollingWatchfalse设置 CHOKIDAR_USEPOLLING=1;遇到 EMFILE 时启用
allowRemoteWebApifalse允许来自非回环对端的可信 authority 访问 Web 管理 API;显式启用后会传递给受管开发 DSH,确保其中的 PDM 也能从同一受信 LAN 使用
discoverLanUrlstrue探测并展示实际可达的本机私网 IPv4 URL;不会修改开发 DSH 的监听配置
stableDshHome当前 $DSH_HOME排队更新最终写入的稳定 DSH home
stableUpdateIdleMs5000所有对话和后台任务停止后,安装与重启前各自需要保持的静默时间
stableUpdatePollMs2500待更新队列与稳定版空闲状态的检查间隔
stableUpdateRetryMs30000可重试安装/重启错误的退避时间
stableUpdateMaxAttempts3自动重试上限;达到后必须显式重试
autoRestartStabletrue安装后自动重启稳定版 systemd user service
stableSystemdUnit可选的明确稳定版 .service 单元;为空时只接受以 dsh 开头的 cgroup 叶子 service,不匹配祖先单元
presets{}团队公共 Plugin 集合;值会按顺序传给 dsh plugin add
defaultPresetWorkspace 未声明 preset 时使用的默认名称

Agent 工具

工具作用
dsh_dev_create安装依赖、构建、创建隔离 profile、link Plugin、dump-config
dsh_dev_workspace_create从开发清单或显式成员列表创建多 Plugin Workspace
dsh_dev_start启动 Client watcher 和开发 DSH,等待健康
dsh_dev_stop受控停止开发 DSH 和 watcher
dsh_dev_remove经确认后停止并注销单个实例;可选清理开发数据
dsh_dev_restart只重启开发实例
dsh_dev_status查看单个项目的状态、URL、PID 和错误
dsh_dev_list查看全部项目
dsh_dev_logs读取 Host、watch、check、validation 或 supervisor 日志
dsh_dev_check执行 package.json 中的验证 script
dsh_dev_pack执行检查并输出 tarball
dsh_dev_validate在全新 DSH_HOME 中安装 tarball、dump-config、启动并检查健康
dsh_dev_promote通过 clean validation 后保存带 SHA-256 的发布候选
dsh_dev_queue_update经确认后验证、晋级并记录稳定版待更新版本,等待稳定版空闲后安装并重启
dsh_dev_retry_update经确认后清除失败退避,重试持久化的安装或重启
dsh_dev_shutdown经确认后停止 supervisor 及其管理的全部开发实例

示例对话:

为 /work/dsh-foo 创建开发 Workspace。
启动 dsh-foo Workspace。
查看其中 dsh-plugin-common 的 Watcher 日志。
运行检查,成功后验证并晋级。

dsh_dev_create 的关键参数:

  • pluginPath:目标 Plugin 路径,必填。
  • id:稳定项目 ID;省略时从 package name 推导。
  • install:默认 true。存在 pnpm-lock.yaml 时使用 --frozen-lockfile
  • build:默认 true。存在 build script 时自动执行。
  • watchScript:默认探测 dev:client
  • checkScript:默认依次探测 checktest

dsh_dev_workspace_create 的关键参数:

  • workspacePath:Workspace 根目录,必填。
  • id:稳定 Workspace ID;省略时依次使用清单 name 和 primary package name。
  • preset:稳定控制面配置中的公共 Plugin 集合。
  • plugins:显式成员列表;提供后会覆盖仓库开发清单。
  • install / build:所有本地成员的默认行为;成员级配置可以覆盖。

dsh_dev_remove 必须传入 confirm: true。默认只停止并从活动列表注销实例,registry 会保留 ID、原 Workspace 与受管数据的所有权记录,原有 DSH_HOME、日志和产物仍留在磁盘;之后只有同一 Workspace 能用该 ID 重新接管,不同 Workspace 会被拒绝,避免继承旧 profile。对已注销 ID 再传 purge: true 也可清理保留数据。purge 会删除 Manager 管理的实例 home、当前 Workspace 的已知日志、顶层 tarball、依赖包和 validation 目录;artifacts/<id>/releases/ 中的晋级 tarball、历史元数据及 latest.json 始终保留。该操作不会删除 Workspace 或 Plugin 源码。

Web 面板分别提供“注销”和“清理数据”:前者保留全部磁盘数据,后者使用 purge: true;两者使用不同确认文案,成功后会关闭该项目已打开的日志面板。

执行 purge 时,Manager 会先把注销和 cleanup tombstone 原子写入 registry,再清理磁盘,最后移除 tombstone。清理中断或部分失败时,dsh_dev_list 会以 removing 状态显示该 ID;只有再次传入 purge: true 才会继续幂等清理,默认的 purge: false 不会越过原始保留契约。cleanup 未完成前不能用同一 ID 重新创建。remove 遇到 RPC 传输错误不会自动重放,而会提示先调用 dsh_dev_list 判断注销是否已经提交。

状态与故障处理

Workspace 状态包含:

stopped → initializing → starting → ready
                         ↘ failed
                         ↘ orphaned
removing → retry cleanup → removed

orphaned 表示端口有进程,但当前 supervisor 没有对应的子进程句柄。Manager 会拒绝停止或删除该实例,避免 PID 或端口复用时误杀其他程序,或在未知进程仍使用实例数据时清理磁盘。先核对端口和日志,再手动处理该进程。

开发 DSH 中的运行中任务在 Host 重启时仍会中断。Manager 保护的是稳定控制面和其他开发实例;调用 dsh_dev_restart 前仍应确认目标开发实例没有需要保留的任务。

validatepromote 每次都使用新的验证目录。primary 和本地 member 会分别打包为 .tgz,Preset 与固定 source 随后一起安装;验证过程不会复用 link 开发环境。晋级目录包含 primary 的不可变 tarball、时间戳元数据和 latest.json。单独调用 promote 不会修改稳定 DSH。

dsh_dev_queue_update(confirm=true) 会把晋级产物写入 stateDir/stable-updates.json。同一 npm package 只保留最新候选,不同 package 可合并;稳定 Host 同时检查所有 live agent 的 idle/running、agent maintenance 和 owner/unowned background jobs。第一次完整静默窗口后,supervisor 校验 tarball 的 releases 路径、SHA-256 和内部 package.json 名称,再逐个安装到稳定 Web profile;每次执行命令前先写 applying journal,每个 package 成功后立即 checkpoint,因此批次中途失败不会丢失已安装/未安装边界。profile 安装只修改磁盘,不改变当前进程;若最后一次空闲检查后有新工作开始,它仍继续使用当前已加载版本。随后重新等待一个完整静默窗口。重启交接前,Coordinator 使用 agent maintenance 栅栏锁住现有 Agent,并同步接管新创建的 Agent;此后到 Host teardown 之间的新消息只会排队,不会先开始再被重启打断。新 Host PID 启动后,Coordinator 优先要求它报告与安装后相同的 Web profile generation。若 generation 仅因其他合法 profile 更新而变化,则必须同时确认当前磁盘仍与 Host 启动快照一致、且本批次每个 bundle 的 manifest dependency 与 lockfile resolution 都仍精确引用原不可变 tarball,才把批次标记为已应用;目标 bundle 被移除、替换或在 Host 启动后再次漂移时仍会 fail-closed 并要求显式处理。

队列、applying journal 和“已安装、待重启”状态都通过临时文件 fsync、rename、目录 fsync 后再切换内存状态。安装/重启失败按配置有限退避,达到上限后使用 dsh_dev_retry_update(confirm=true) 显式恢复;supervisor 恢复时若发现结果未知的 applying journal,或旧版待重启记录缺少 install-time profile generation,则禁止自动处理,必须显式确认。更新类非幂等 RPC 在响应丢失时不会盲目重发。自动重启只接受显式 stableSystemdUnit/proc/self/cgroup 的唯一叶子 .service,拒绝 scope 中的祖先 service。systemctl 使用阻塞式 restart:明确失败时释放栅栏并记录错误,成功路径由 Host teardown 和新 PID 确认。protocol v5 以前的 supervisor 不支持此能力时,先保存开发实例中的工作并执行 dsh_dev_shutdown(confirm=true),让下一次调用启动新版 supervisor。稳定更新 Web API 只接受回环对端。

安装 PDM 后,它会自动向 DSH 的全局 system prompt 注册稳定更新弱护栏,不需要再安装独立 prompt Plugin。规则按行为而不是按某条宿主命令描述:稳定 profile 的修改、包替换以及 Host、supervisor、service、container、process 或 machine 的中断都必须走 PDM 队列和 idle gate;队列不可用时 Agent 必须停止并请求明确的 fallback 选择,不能自行换用 shell、文件系统、包管理器或平台 API 绕过。该机制会影响后续 prompt 组装,包括已有会话的后续 turn,但它是模型约束,不是 OS 权限隔离,不能替代强制执行。

安全边界

  • 所有子进程通过参数数组启动,shell 固定为 false
  • allowedRoots 在真实路径解析后检查,可拦截越界路径和符号链接逃逸。
  • supervisor 只监听 127.0.0.1,RPC 使用 256-bit 随机 bearer token。
  • 控制面 RPC 通过 Node 原生 loopback HTTP 发出,不读取代理分发器。
  • supervisor 元数据与配置以 0600 权限写入。
  • Web 管理 API 默认同时要求 loopback 对端与可信 authority。只有显式启用 allowRemoteWebApi 后,非回环对端才可通过 webRuntime.trustedHosts authority、Fetch Metadata 与 Origin 篱笆访问;Origin 必须同时匹配当前 transport scheme 与 authority,远端伪造 Host: 127.0.0.1 不会被当成本地请求。写操作还要求同源 Client 使用的自定义请求头。
  • allowRemoteWebApi 是可信网络 opt-in,不是用户认证;启用后,同一受信网络中的原生 HTTP 客户端仍可能伪造浏览器头并执行管理操作。必须配合防火墙、VPN 或其他网络访问控制,不能把端口直接暴露到不可信网络。该显式 opt-in 会通过受管环境变量传给开发 DSH,但默认 false 不会被自动放宽。
  • discoverLanUrls 只展示经 TCP 探测实际可达的私网 URL,不绕过 DSH 对 --host 0.0.0.0 的安全限制。LAN 监听必须由开发 profile 显式启用(例如放入受信的 LAN-access Preset),并配合防火墙或 VPN;这不是用户认证。
  • package script 名称经过白名单校验。
  • 日志和命令输出有大小限制。
  • 所有本地 Plugin 都必须声明 dsh.bundle
  • 端口冲突时启动失败,不会接管未知进程。
  • 停止后会确认受管 Host/watcher 进程句柄对应的 PID 已退出且端口关闭;任一受管进程仍存活时拒绝注销或清理。目标脚本自行 daemonize 的后代进程不在句柄保证范围内,因此只管理受信任的 package scripts。子进程退出回调会立即清除受管句柄,降低 PID 复用导致的误信号风险。
  • 删除要求传入与列表完全一致的规范化实例 ID;加载 registry 时也会重新验证项目和成员 ID。清理路径只由 stateDir 和这些 ID 推导,每次删除都校验受管根目录和 containment,不信任 registry 中的 dshHome,也不会跟随实例或 artifact 根目录的符号链接;日志按已知精确文件名清理,避免相似 ID 互相误删。
  • purge 保留整个 artifacts/<id>/releases/,不会把晋级产物与临时验证数据一起删除。

安装依赖和执行 package script 会运行目标仓库中的代码。只管理受信任的 Plugin 仓库,并把 allowedRoots 缩小到实际开发目录。

开发与验证

pnpm install
pnpm run typecheck
pnpm test
pnpm run build
pnpm run test:e2e
pnpm pack --dry-run

test:e2e 会创建临时 Plugin 和临时 DSH_HOME,使用真实 dsh CLI 完成 link、dump-config、Web 启动、HTTP 健康检查、Manager 重连、受控停止、clean validation、晋级和实例清理,并在 purge 后重新校验晋级 tarball 的 SHA-256、历史元数据和 latest.json。单元测试还覆盖多 Plugin 组合、Preset、YAML 清单、多个 watcher、并发删除、cleanup 恢复、进程存活校验、路径防护和代理绕行。

环境覆盖:

DSH_COMMAND=/path/to/dsh \
DSH_DEV_MANAGER_E2E_PORT=43881 \
CHOKIDAR_USEPOLLING=1 \
pnpm run test:e2e

已知边界

  • 当前管理 Web profile。
  • Client HMR 依赖各本地成员持续重写 Client bundle;默认探测 dev:client script。
  • Host 代码变化需要调用 dsh_dev_restart
  • supervisor 进程自身异常退出时,已启动的开发 DSH 可能成为 orphaned;安全策略会保留它并拒绝自动终止。
  • 稳定 DSH 自身退出仍会中断它正在执行的任务;独立 supervisor 与开发实例会继续运行,稳定 DSH 恢复后可重新连接。