npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

dsh-tool-watchtower

v0.1.0

Published

DSH Web GUI plugin: tool & model activity watchtower

Readme

dsh-tool-watchtower

DSH(DeepSeek Harness)出仓插件:一座架在工具调用管线模型调用通道上的「瞭望塔」。 每次工具调用被记录、可按规则门禁(deny / ask)、被计时、可对结果做脱敏/标注/阻断; 每次模型调用被只读观测(provider/model 路由、实时 token 用量、提示词入队)。全部状态经 资源通道推给 Web GUI 的 live 面板(composer 胶囊 + 会话头入口 + 活动详情),并为自己的 报告工具提供会话内自定义卡片。

只观测、不改行为是模型通道的硬边界:不换模型、不改请求、不重放流。

本插件是 docs/tutorial2/(进阶教程)的贯穿示例的实现。 教程正文逐章讲解每个扩展点的语义与坑;本 README 只记录成品形态。

原理

  • 宿主半边src/index.ts + src/host/,TypeScript 源码;node build.mjs 编译为 lib/index.js + lib/host/*.jslib/ 不入库), inject: ['webServer', 'connection', 'tools']
    • tools/result(emit):每次工具调用的只读终局,落进按会话分桶的环形缓冲 (src/host/activity.ts,每会话 256 条、最多 32 个会话,最旧淘汰);
    • tools/pre-execute(waterfall):按插件行 config.rules 做门禁,首条命中生效, 默认放行。deny / ask 会作为 decision 记录自行审计(被拒的调用也会以错误结果流完 管线,靠这条记录与工具自身的失败区分);allow 永远是 next(),绝不亲自断言;
    • tools/execute(waterfall 包裹):给每次调度记墙钟毫秒,按 callId 递给 tools/result 观察者落账(包裹者的 next() 在 post-execute 之前返回,最终真相仍以 tools/result 为准);
    • tools/post-execute(waterfall):对 config.transform.tools 点名的工具做结果变换 ——redact(替换模型/UI 可见投影,规范值不动)、annotate(给下一次模型请求捎一条 notice 上下文)、block-secret(结果级否决 + 纠正反馈)。结构是「先委托、尊重下游 block、再叠加」;
    • llm/stream(waterfall):只读旁听每次模型调用(options 深冻结、原样返回 next() 的流)。该事件没有会话身份,按会话的数字来自下面两站;
    • session/eventrequest/header 类型):耐久的模型路由快照(provider/model), 新值替换旧值;
    • agent/assistant-stream(emit):模型尝试的实时帧流(start/chunk/end),usage 块 折进四个 token 桶(input / output / cacheRead / cacheWrite);
    • agent/inbox/inserted(emit):提示词入队时刻(记字符数,不是 token 数);
    • webServer exact 路由 GET /dsh-tool-watchtower/ping:JSON 心跳(含跟踪的会话数);
    • connection.fetch 已鉴权路由 GET /api/dsh-tool-watchtower/activity?session=<id>: 活动快照(revision 单调递增;未知会话返回空快照而不是 404);
    • tools:注册 watchtower_echo(变换模式的练功靶)与 watchtower_report(读哨塔 快照,带 presentCall/presentResult 的 generic 卡呈现)。
  • 客户端半边src/client/index.tsx → 构建为 lib/client.js,单文件无相对 import), inject: ['slots', 'locale', 'resources']
    • resources provider:把 dsh-resource://dsh-tool-watchtower/<会话> 变成帧流—— 每秒带修订号的跟随轮询(修订号没变不产帧),失败走 { ok: false } 帧、abort 贯穿 到 fetch;
    • conversation.composer.dock 座位:活动胶囊(tools/错误/拦截计数 + token 流量), 点击弹出 Menu(类型过滤 + 详情入口);
    • conversation.session.header.utilities 座位:会话头入口按钮,打开同一个活动面板 (同地址第二个消费者,注册表按引用计数只开一条轮询流);
    • tool.call.toolview keyed 座位(key: 'watchtower_report'):报告工具的会话内 自定义卡片(运行中 ongoing 点 / 完成后一行摘要 + done 点);
    • 门禁 deny / ask 计数变化时顶部滑 Toast(首次读数算历史不算新闻);
    • 弹层全部用 @deepseek-ai/dsh-client-ui-primitives(Menu/Modal/Toast/StateDot), 样式只用 --dsw-alias-* 语义 token,深浅色自动跟随。

配置

| 键 | 类型 | 默认 | 说明 | | --- | --- | --- | --- | | rules | array | [] | 门禁规则表,首条命中生效tool 为工具名精确匹配或 '*' 通配(通配条目应垫底);decisionallow/deny/askreason 可选(deny 时成为模型可见的错误文案,建议给模型指路) | | transform.tools | string[] | [] | 允许变换的工具名单(只应点自己的演示工具) | | transform.mode | string | off | off / redact / annotate / block-secret |

非法配置在插件加载时抛错终止(fail loudly),终端日志可见。示例:

- insert:
    - id: watchtower
      name: '/absolute/path/to/dsh-custom/dsh-tool-watchtower/lib/index.js'
      config:
        rules:
          - tool: web_fetch
            decision: ask
            reason: outbound fetches need a human nod
          - tool: write
            decision: deny
            reason: this session is read-only, use read instead
          - tool: '*'
            decision: allow
        transform:
          tools: [watchtower_echo]
          mode: redact

只改 config:重新组合 + 刷新页面即可;新增插件行或改代码:重启 dsh web + 刷新 (改 lib/client.js 也必须重启)。

启用

方式 A(本机挂载):编辑 $DSH_HOME/profiles/web/cordis.patch.ymlname 用绝对路径 (见上例;不带 config 时至少给出 id 与 name 两行)。构建并重启:

cd dsh-tool-watchtower
pnpm install     # 一次性:装入 typescript devDependency(node_modules/.bin/tsc)
node build.mjs   # 两个半边的产物统一重建到 lib/

方式 B(npm 包,已发布):本插件声明了 dsh.bundle,按包名安装即插入插件行:

pnpm dsh plugin --profile web add dsh-tool-watchtower

方式 C(git 子目录安装):本仓库根是插件集合、不是单包,需用 pnpm 的 #path: 写法 指向插件目录。注意 lib/ 不入库:git 渠道装到的包没有构建产物,装完需在插件目录内 pnpm installnode build.mjs,因此只适合本仓库协作者;对外分发用方式 B(npm 包在发布前已重新构建,产物随 tarball 走):

pnpm dsh plugin --profile web add 'github:pangzi-club/dsh-custom-plugins#path:dsh-tool-watchtower'

三种方式安装或变更后都需要重启 dsh web 并刷新页面。关闭:方式 A 删除插件行;方式 B/C 执行 pnpm dsh plugin --profile web remove dsh-tool-watchtower

发布

npm publish(在 npm 上 dsh-tool-watchtower 名字可用,包名与插件名、客户端模块 id 一致,无需改名)。prepublishOnly 会先 node build.mjs && npm test(42 项全绿才允许 上传);发布内容由 files 白名单约束(lib/ 两个半边 + cordis.patch.yml),src/test/ 不进包。首次发布前跑一次 pnpm install 装入构建工具。

验证

curl http://127.0.0.1:3080/dsh-tool-watchtower/ping
# {"ok":true,"plugin":"dsh-tool-watchtower","sessions":0,"now":"…"}

GUI 内:composer 下方出现瞭望塔胶囊;让模型跑一个工具,胶囊计数约 1 秒内 +1;菜单选 「查看详情…」看记录列表;配置 deny 规则后让模型撞门,顶部应滑过拦截 Toast。

测试

npm test    # node --test,42 项,零依赖、不打网络、不依赖 GUI

测试跑在构建产物上:改了 src/node build.mjsnpm test。分层:纯函数 (rules / transform / activity)、编排(waterfall harness 组合瀑布链,断言委托/短路/ 先委托后叠加/尊重下游 block 四条纪律)、信封(bundle 三座位 + provider + 词表对齐)。

已知限制

  • 未经真实运行实测的行为:事件载荷与组件 props 依据上游文档与源码签名撰写(教程 各章「延伸阅读」标明出处),本插件的 GUI 侧行为尚未在真实会话中逐项验证;遇到与 教程不符处以 docs/tutorial2/ 各章排查顺序 + 上游文档为准,并欢迎回修。
  • annotate 的 notice 是手写字面量:上游造 UserMessage 的正路 createUserMessage@deepseek-ai/dsh-llm)出仓解析不到;若未来版本对消息 id 形态加运行时校验,这里是第一个会碎的地方。
  • live 通道是轮询:provider 在浏览器、数据在宿主,帧流靠每秒一次的带修订号轮询 供血(不是真推送);每个开着面板的会话每秒一个请求,引用计数保证无订阅即无请求。 要细粒度需改走 ctx.connection.rpc 推送通道。
  • ask 的可用性取决于审批面:ask 把裁决交给 ctx.approval,fail-closed——没有 可用审批 answerer 的组合里 ask 等同于拒绝,不是 bug。
  • 内存有界但进程内:环形缓冲(256 条/会话 × 32 会话)只活在宿主进程里,重启即失; 历史统计请用 dsh-stats(读耐久日志)。
  • chars ≠ tokens:提示词记录是字符数,token 化是 provider 的事;token 口径来自 assistant-stream 的 usage 块,是估算观测,不是平台账单。