All plugins

DSH / BUNDLE / BUNDLES

dsh-gov-workbench

v1.0.0zbzbhshs / dsh-gov-workbenchbb9d7716ba

InstallableBundlesBundles & other modulesCommunity · Topic auto-analysis

Overview

dsh-gov-workbench

政务门户风格的 dsh Cordis 插件:在独立端口提供一个办事大厅界面,直接对接宿主的会话、模型与权限。

README / EN

Package documentation

Registry summary

政务门户风格的 dsh Cordis 插件:在独立端口提供一个办事大厅界面,直接对接宿主的会话、模型与权限。

dsh.pub verifies the pinned bundle contract, runtime facts, and distribution semantics. The complete README remains in the source repository.

Read the full README on GitHub

LIMITATIONS

Known limitations

1. 3091 端口已在真实 dsh 进程中挂载验证(0.2.0-rc.2 / Windows),插件随宿主进程启动后正常监听并返回页面。回归测试 `node test/plugin-boot.mjs` 用 mock ctx 与假网关覆盖装配路径,它先让内核分配一个空闲端口再写进配置,因此不会占用 3091(`port: 0` 会被 `mergeConfig` 当作非法值回落到默认 3091,所以测试不能用它)。 2. dsh 0.2.0-rc.2 上 `ctx.apiProxy` 不存在。`@deepseek-ai/dsh-host-apiproxy` 已从发行版移除,API 网关被重构为 `ctx.typertGateway`(`@deepseek-ai/dsh-api-gateway` 的 `TypertGatewayService`)+ 各域 controller(`sessionController` / `settingsController` / `workspaceController` / `agentPresets` / `permissionPresets` / `llm` / `userQuestions` / `approval` 等);端点清单由 `@deepseek-ai/dsh-typert-registry` 在运行时从各包的 `typert.host.js` 注册进 `ctx.typert.local`。因此 `lib/index.js` 的 `inject` 留空(原因见下面第 4 条),挂载由 `lib/host.js` 把两种宿主形态归一成同一内部接口(`invoke` / `stream` / `hostEvents` / `resolveEventResult` / `describeEndpoint`),同时兼容 0.2.0+ 的 `typertGateway` 与 0.1.x 的老形态 `apiProxy`(后者优先级更高)。两者都不存在时插件照常开页面,并在状态接口里报告 `hostAvailable: false`,而不是崩掉整个 dsh。 配套的惰性解析设计:因为插件是「立即激活」的,网关可能在 `apply()` 之后才 provide。所以 `lib/host.js` 不在构造时绑定一次,而是: - `kind` 是 getter,每次读取都重新探测,实时反映可用性; - `invoke` / `stream` / `resolveEventResult` / `describeEndpoint` 在每次调用时重新 `ctx.get('typertGateway', false)`; - `hostEvents(signal)` 是等待式生成器:网关未就绪时先等(响应式 `ctx.inject([...], cb)` + 轮询兜底),出现后再开始转发;流自然结束后若网关仍在且未 abort 会重新接续,因此 `MuxController` 不需要任何重启逻辑。 回归测试:`test/plugin-boot.mjs` 的「网关晚于插件 provide 时自动接上」与「网关缺席时 hostEvents 等待而不是立即结束」两项。 3. 会话事件不在 0.2.0-rc.2 的网关转发白名单里。`@deepseek-ai/dsh-api-remotes` 只把一份白名单事件转发给浏览器(`approval/request`、`user-questions/request` 以及各类配置变更),session 事件不在其中;0.1.x 的 `apiProxy.events.mux()` 也已不存在。所以 `lib/sse.js` 的 `MuxController` 改为在宿主进程内直接订阅 `ctx.on('session/event', ..., { global: true })`(并订阅 `session/created` / `session/disposed`),与 `dsh-session-controller` 自己 follow 会话时的做法一致;`{ global: true }` 拿全局可见性,老版本自动退回两参数形式。实时投影(统计 / 标题 / 待办)另经 `sessionProjections.onChanged` 获取。若宿主未挂该投影服务,插件告警但继续工作,统计与待办只随会话事件更新。 4. **`cordis.patch.yml` 的插件行不能写 `inject` 字段,尤其不能写 `!!js`。** 写错会让插件永不激活,启动日志停在「Plugins waiting for services」,3091 根本不监听。正确做法是**整行删掉 `inject:`**(不是写 `inject: []`,空数组同样会覆盖插件导出的值),让 `lib/index.js` 的 `export const inject = []` 生效。`inject: [apiProxy]` 这种普通数组写法在 0.2.0-rc.2 上同样会让插件永不激活,因为该版本已无 `apiProxy` service。两种写法都不能用,原因不同。完整的五步机制、复现输出与回归守卫(`test/patch-inject.mjs` 8 项,含对照组)见 [`docs/harness-integration.md` 第 14 节](docs/harness-integration.md);`test/syntax-check.mjs` 另有一条静态守卫。 5. `test/syntax-check.mjs` 在受限沙箱下会自动降级。该脚本用 `node --check` 逐文件校验;若沙箱拒绝子进程的管道 stdio(`EPERM`),脚本会自动退回 `stdio: 'inherit'` 模式仅取退出码,并在输出里注明降级(校验方式仍是同一个 `node --check`)。本机 danger-full-access 模式下不会触发降级。 6. 端口 / host 改动不热生效。`PUT /plugin/config` 会落盘并返回 `needsRestart: true` 与提示文案,但当前监听保持不变,需重启 dsh。 7. `public/` 资源不缓存但每次读盘。好处是改完刷新即生效、无需重启;代价是每个静态请求都有一次磁盘读。适合本机单用户场景。 8. `req.signal` 的版本差异(三版本对照)。参考实现把 `req.signal` 传给宿主方法,这在任何 Node 上都不可靠:Node 22 及以下没有 `.signal`,宿主帧队列的 `signal.addEventListener(...)` 没有 undefined 防御,会抛 `TypeError`,前端重连后再次抛错,形成死循环;v24.18.0 会 abort,但总在 `res.close` 之后;v24.21.0 正常结束根本不 abort。三者都不能用于流中途取消:v22.22.2 连信号都没有,v24.18.0 太晚,v24.21.0 连结束都不反映。因此 `lib/transport.js` 的 `trackAbort()` 一律自建 `AbortController`,在 `res.on('close')` 里 abort,并用 `res.writableEnded` 区分「客户端断开」与「正常结束」。`test/server-smoke.mjs` 第 5 组是可复现探针,只断言三版本都成立的部分(进入处理器时尚未 abort),不对「结束后一定 abort」做断言,否则换 Node 版本 CI 会红。逐版本对照表与探针脚本见 [`docs/harness-integration.md` 第 7 节](docs/harness-integration.md)。全部 5 个测试套件已在 v22.22.2 / v24.18.0 / v24.21.0 三个 Node 上跑通。 9. 关停必须用 `ctx.effect`,不能用 `ctx.on('dispose')`。cordis 4.x 上 `ctx.on('dispose', fn)` 的 `fn` 永远不会执行,该事件名从不被派发。若用它做清理,插件卸载后 http 服务会继续占着 3091 端口。cordis 的插件级清理语义是 `ctx.effect(() => () => cleanup)`,effect 回调返回的函数在该 fiber 销毁时调用(`@deepseek-ai/cordis-plugin-timer` 等官方插件都是这个写法)。`lib/index.js` 已改用 `ctx.effect`,并有两条回归断言守着(行为断言 + 源码模式断言,且忽略注释)。 10. cordis 的 `ctx` 是 Proxy:读未声明 service 的属性会抛错,不是返回 `undefined`。`ctx.apiProxy` 在 mock ctx 上完全正常,在真 cordis 下直接抛 `cannot get property "apiProxy" without inject`,插件启动即崩。因此 `lib/host.js` / `lib/sse.js` / `lib/bridge.js` 的 service 探测一律走 `ctx.get(key, false)`(`false` = 不要求已注入),