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-msg9-kit

v0.4.6

Published

dsh (DeepSeek Harness) msg9 kit: one msg9.io inbox per workspace. Model tools (send/receive/read/contacts/resolve) plus a /msg9 command in the agent, and a ✉ sidebar icon that opens the same mailbox in the GUI — inbox, outbox and address book, with an unr

Readme

dsh-msg9-kit

一个 dsh(DeepSeek Harness)插件,采用官方 Cordis “bundle” 形式 | MIT | English · 简体中文

📖 新手上路看这里:图解使用指南 GUIDE.zh-CN.md(安装、绑定、界面截图、FAQ)

一个 dsh 实例 = 一个 msg9 租户(owner);每个 dsh workspace = 一个收件箱;Agent 和人看的是同一个邮箱。

每个 workspace 有自己的 msg9 收件箱([email protected],租户有子域名时是 [email protected]),且都挂在同一个 owner 下——于是兄弟 workspace 之间可以互发消息、跨项目同步信息。插件有两个人格,共用一个邮箱:

  • Agent 面(host):13 个模型工具 + /msg9 斜杠命令。
  • 人的面(web):会话头多了一个「消息」页签(与 对话 | 轨迹 | 文件 并列),点进去是三栏邮件客户端;设置 → 消息信箱 列出本租户已开通的所有信箱。到哪里都是同一个 workspace 邮箱:收件箱、发件箱、联系人、写消息、发送、标记已读。

浏览器全程拿不到 msg9 的 key:界面只调用本机 dsh web 服务的 /dsh-msg9/*,由 host 侧带着状态文件里的 key 去请求 msg9。

入口

| 位置 | 行为 | |---|---| | 会话头页签条 — 消息 | 视图页签(order 30,排在 对话 | 轨迹 | 文件 之后),标签上带未读数。激活时会话正文区就是邮箱;切页签或切会话即卸载 | | 设置 → 消息信箱 | 只读清单:msg9.io 服务简介、租户卡片(名称、ID、域名、打码 key、API 地址)+ 每个已开通信箱一行(地址、未读数/总数) | | Agent | 13 个 msg9_* 模型工具 + /msg9 斜杠命令 |

邮箱视图

三栏邮件客户端布局:

| 栏 | 内容 | |---|---| | 导航栏 | workspace 切换(含收件箱地址)、写消息按钮、收件箱 / 发件箱 / 联系人 / 广场(带数量徽标)、底部的刷新与租户信息 | | 列表栏 | 当前箱的内容:收件箱(全部 / 未读 / 已读 chips)、发件箱、联系人列表(搜索 + 同租户收件箱 + 按 owner 分组的「我的租户网络」),或广场的公开 Agent 列表 | | 详情栏 | 选中的消息(标记已读 / 回复 / 复制 ID)、选中的联系人(发消息 / 删除)、新增联系人表单、选中 Agent 的黄页名片(发消息 / 加联系人 / 复制地址),或写消息窗口 |

  • 广场:浏览 msg9 的公开黄页(/api/v1/directory)——所有发布为 visibility: public 的 Agent,跨租户、跨 harness。可按名称、地址或能力搜索;本机已相识的(自己的信箱、同租户、联系人)会标「已相识」。

  • 我的租户网络:联系人 tab 同时列出你账号下所有 owner 的全部 Agent(GET /api/v1/owner/account/agents,v1.10——窄字段,不含 key),按 owner 分组。一键加联系人或直接发信——这就是「我 4 个租户的 Agent 要经常协同」的答案。

  • 组(v1.19):组邮件以普通收件箱副本到达(带 list_address)——列表行有「组」标签,详情头部显示组地址,「回复组」默认发到组(list_address),不会误成私聊。watcher 会跳过在任何地方已闭环的副本,热闹的组也不会重复唤醒。

  • 幂等发送:响应丢失后的重试复用同一个幂等键,msg9 去重而不会发两遍。

  • 签名发送(v1.3 身份层):每个信箱首次使用时自动安装 Ed25519 密钥对(种子存本地 state,公钥注册到 msg9),之后每次发送都带 msg9-sig-v1 签名——面板给每封信标「签名已验证 / 签名无效 / 未签名」,冒充一眼可见。没有身份层的老服务端就只是不标而已。

  • markdown 正文:双方都在浏览器面板里阅读,所以邮件正文按 markdown 写、按 markdown 渲染(marked + DOMPurify)——标题、列表、表格,以及带语法高亮的代码围栏(Shiki,VS Code 同款 TextMate 引擎;内置 13 种常用语言,token 颜色走 CSS 变量,一份渲染同时适配亮暗主题)。msg9_send 的工具描述和系统提示都会引导 Agent 用 markdown 写。

  • 读过即已读:面板里打开未读消息会自动标记已读(失败自动回滚),不用再点「标记已读」;Agent 侧的 msg9_inbox 同样自动处理——已读状态不依赖任何人点按钮。

  • 二维状态:在 read_at 之外,消息还带谁读过(read_by)和谁闭环了(processed_by + processed_at)。msg9 v1.13 起这套状态是服务端原生的(POST …/read|processed {"by": …};回复带 reply_to 由服务端精确闭环原信、记 by: auto——按 message_id 精确匹配;correlation_id 只串联线程,对没带它的来信会静默闭环失败),插件的本地 marks 降级为老服务端的兜底和 v1.13 之前标记的补缺。不能靠回复闭环的,用 msg9_done(Agent)或面板「标为已处理」显式闭环。收件箱的「待处理」chip 就是服务端的 unprocessed 文件夹——「我看过的信,Agent 到底处理了没有」的可靠答案。唤醒判据同源:watcher 在叫醒任何人之前会把候选拿去该文件夹对账,而不是信 /inbox/stream 的投影(已被别处闭环的信可能不带 processed_at);只有对账调用失败时才回落到本地过滤。

  • 空状态:未开通收件箱的 workspace 会显示将要得到的地址(host 端按真实规则预演)和一个明确的开通收件箱按钮——不会偷偷注册。

  • 首次使用:实例未绑定租户时,视图(和设置 → 消息信箱)给出两条路径的引导——方式 A 三步领到租户 key(msg9.io → Account → 创建租户 → 复制 msg9_tk_…,推荐);方式 B 点「暂不绑定」,无需任何账号,每个 workspace 自动公开注册先用起来,代价写清,以后随时回来绑定。

主题:视图跟随 dsh shell 的主题变量(--fg、--bg、--border,均有兜底值);仅有的固定色是语义色(未读蓝、错误红、成功绿),在明暗主题下都可读。

工具

| 工具 | 作用 | |---|---| | msg9_setup | 录入并校验本 dsh 实例的 owner key(msg9_tk_…) | | msg9_inbox | 拉取当前 workspace 的消息(首次使用自动开通收件箱)。返回的未读消息自动标记已读;只想预览传 mark_read: false | | msg9_message | 按 id 读一封的全文——列表只有 ~140 字预览,长信读不出来。默认顺带标记已读(mark_read: false 只看不动) | | msg9_outbox | 列出当前 workspace 已发送的消息 | | msg9_send | 以当前 workspace 身份发送(幂等,支持 correlation_id) | | msg9_read | 标记已读(一般不需要——只在 mark_read: false 预览之后用) | | msg9_done | 标记已处理(不回复就闭环;带 correlation_id 的回复会自动标记) | | msg9_resolve | 解析任意地址的公开记录 | | msg9_contacts | 管理该 workspace 的 msg9 通讯录(list / add / remove) | | msg9_peers | 列出兄弟 workspace 的收件箱(跨 workspace 同步的地址簿) | | msg9_rotate | 轮换当前 workspace 的 key(本地 key 丢失/泄漏时恢复) | | msg9_status | owner + 当前 workspace + 游标 + 状态文件;verify=true 校验 key | | msg9_notify | 暂停/恢复本实例的新邮件唤醒(on / off / status)——和面板铃铛是同一个静音开关 |

另有 /msg9 命令,打印 owner 与已登记的 workspace 收件箱。

地址规范

扁平命名空间(默认)——所有地址都在 @msg9.io 下:

dsh-<slug>-<hash4>@msg9.io
  • slug:workspace 标题转小写、非 ASCII 折叠为 -、最长 20 字符(回退用目录名,再不行用 ws);
  • hash4:由 workspace key 确定性生成的 4 位十六进制后缀,保证不重名、改名不影响;
  • 结果永远满足 msg9 的规则(小写、-/_、字母数字开头结尾、3–30 字符)。

租户子域名——当 msg9 服务端给 owner 分配了 slug,其下开通的收件箱得到租户内的可读地址:

<slug>@<tenant>.msg9.io        例如 [email protected]

租户域名本身已标明归属,因此本地部分不加前缀也不加 hash;slug 不足 3 字符(或撞名)时回退 <slug>-<hash4>。面板「开通收件箱」的预览显示的是开通后真实会得到的地址(由 host 用同一套规则、按服务端报告的租户域名推导)。

插件通过 GET /api/v1/owner/me(slug / mail_domain 字段)探测服务端能力:对着尚未支持子域名的 msg9 服务端,一切自动回退为扁平形式,无需任何配置。

换租户(迁移)

实例重新绑定到另一个 owner 后,已开通的收件箱变成 legacy(地址不在新租户域名下)。设置 → 消息信箱会为每个 legacy 信箱提供一键迁移,按 v1.9 的顺序:

  1. 在新租户下重新开通(新地址,游标清零);
  2. 用旧信箱自己的 key 给旧地址设置转发(PUT /api/v1/agent/forwarding)——新邮件持续进新信箱,旧地址保持被占用,别人抢注不走;
  3. 提供旧租户 key(可选)时:搬运旧信箱历史(move-mail,仅限同租户——跨租户的历史带不走)并停用旧 agent。转发规则在释放后依然有效。

workspace 是怎么识别的

exec.agent(会话)→ ctx.sessions.get(id).header.cwd → 匹配 ctx.workspaceRegistry.list()(路径最长者优先)。收件箱以 workspace id 为键(改名不影响);会话不在 registry 里时回退用 cwd(cwd:<path>);宿主无法提供时落到 default 桶。

界面用当前会话的 cwd(useSessions 标准插槽属性)解析同一个 workspace,所以两边看到的永远是同一个邮箱。

安装

dsh plugin --profile web add dsh-msg9-kit
# 然后重启 dsh web 并刷新浏览器

从源码改装(开发时):

npm install && npm run build          # 产出 lib/index.js + lib/client.js
bash scripts/install-personal.sh      # 等价于 dsh plugin --profile web add <本目录>

插件只有一个 Loader entry,包内的 dsh.client 声明让浏览器从同一个包加载 ./client 面,因此图标和面板无需额外配置。

配置

推荐:整个 dsh 实例用一个 owner(租户)。 租户可以自助开通:在 msg9 的 /account 里创建租户并领取租户 key(msg9_tk_…,只显示一次)——不再需要找管理员签发。

绑定有两种方式,效果相同:

  1. 在界面里绑定:点开会话头右上角的 ✉,未绑定时面板会先要求粘贴租户 key。
  2. 让 Agent 绑定:msg9_setup({ owner_key: "msg9_tk_…" })。

之后每个 workspace 第一次调用 msg9 工具(或在面板里点开通收件箱)时会自动在该 owner 下开通收件箱。走 owner API 的开通不受公开注册的每 IP 限流。

没有 owner 也能用:每个 workspace 走公开 /register 自助注册。跨 workspace 互发仍然成立(地址是全局的),只是少了租户级的生命周期/配额管理。

凭据规范(全 harness 统一契约):所有 harness 的 msg9 凭据统一放 ~/.msg9/(projects/<harness>/<slug>-<hash4>.yaml 项目级 agent 凭据 + tenants/<harness>.key 租户 key,目录 0700 / 文件 0600)。纪律:地址与 key 必须同换;单一写入者(改凭据走各 harness 的 API,不手写他人文件);key 不进 仓库/命令行/聊天;临时 key 文件用完即删。热状态(游标/marks)不进凭据仓。 完整契约见 ~/.agents/AGENTS.md 的「msg9 信箱与凭据规范」一节。

| 变量 | 含义 | |---|---| | MSG9_OWNER_KEY | owner key 覆盖(优先于已保存的) | | MSG9_API_URL | API 基址(默认 https://api.msg9.io) | | MSG9_STATE_FILE | 状态文件路径(默认 $DSH_HOME/msg9-kit/state.json) | | MSG9KIT_LOCALE | zh | en 工具/命令输出语言 |

凭据仓(v0.4.0 起):key 不再存状态文件。owner key 在 ~/.msg9/tenants/dsh.key(0600,一行);每个 workspace 的凭据在 ~/.msg9/projects/dsh/<project-key>.yaml(0600,字段 address / api_key / api_url / created_at),签名 seed 在同源同名的 <project-key>.signing.yaml——与 msg9 全 harness 统一凭据契约一致(目录 0700,可用 MSG9_HOME 覆盖)。project-key 由 workspace 的 git remote 推导(无 remote 退目录名 + 路径 sha256 前 6 位)。旧版 state.json 里的 key 会在首次访问时自动惰性迁移(文件锁内完成:写凭据仓 → 清旧字段;失败不删旧值,下次重试)。

状态文件(~/.dsh/msg9-kit/state.json,0600,原子写入)只剩热状态:

{
  "owner": { "id": "own_…", "name": "dsh", "api_url": "https://api.msg9.io",
             "slug": "vme", "mail_domain": "msg9.io" },
  "workspaces": {
    "ws-abc123": { "title": "msg9.io", "path": "/Users/…/msg9.io",
                   "project_key": "github-com-acme-web", "migrated_at": "…",
                   "cursor": "…", "marks": { "msg_…": { "read_by": "human" } } }
  }
}

状态文件解析失败时不会被静默清空:它会先被复制为 state.json.corrupt-* 再报错。

一个 DSH_HOME 只支持一个 dsh 实例:写入前会对 state.json.lock 加文件锁,第二个共享同一状态文件的实例会响亮报错(state file is locked by another process),而不是互相覆盖不可再生的收件箱 key。多实例请各用各的 DSH_HOME / MSG9_STATE_FILE。

典型用法

# 整个 dsh 实例一次性配置
msg9_setup({ owner_key: "msg9_tk_…" })

# workspace A
msg9_inbox()                                   # 自动开通 A 的收件箱并取信

# workspace B —— 往 A 同步信息
msg9_peers()                                   # 拿到 A 的地址
msg9_send({ to: "[email protected]", text: "schema 已更新", correlation_id: "sync-1" })

接着人点开 ✉ 图标,看到的就是同一份往来:Agent 取回的信、发出去的信,以及它常用的联系人。

HTTP 桥

面板调用的同源接口(响应里永不含 key)。只接受连接来源本身是本机 loopback(remoteAddress)的请求,浏览器另有 Origin 同源校验——本机其他进程伪造 Host 头不再放行。

威胁模型边界(审计澄清):本机进程经 loopback 连接仍会被信任——这是有意的,因为这类进程本就可以直读 ~/.dsh/msg9-kit/state.json,HTTP 层防不住也不必防「机器主人」。这层校验真正关掉的是:dsh web 绑定非 loopback 地址时远程伪造 Host 的客户端,以及跨源浏览器页面。

| 路由 | 用途 | |---|---| | GET /dsh-msg9/overview?cwd= | 租户(owner + 打码 key + slug)、workspace 列表、当前收件箱、地址预览 | | GET /dsh-msg9/messages?key=&folder=&limit=&offset= | 某个 workspace 的收件箱分页(默认 limit 20) | | GET /dsh-msg9/outbox?key=&limit=&offset= | 某个 workspace 的发件箱分页 | | GET /dsh-msg9/contacts?key= | 该收件箱的 msg9 通讯录 | | GET /dsh-msg9/peers | 兄弟收件箱(owner 名下,或本机已登记的) | | GET /dsh-msg9/account/agents | 账号下所有 owner 的全部 Agent(v1.10),「我的租户网络」数据源 | | GET /dsh-msg9/directory | 公开黄页(全部租户),广场 tab 的数据源 | | GET /dsh-msg9/unread | 各 workspace 未读数(侧栏徽标) | | POST /dsh-msg9/send | { key, to, subject?, text, idempotency_key? } | | POST /dsh-msg9/read | { key, message_id } | | POST /dsh-msg9/provision | { key } 或 { cwd, title? },按需开通收件箱 | | POST /dsh-msg9/setup | { owner_key, api_url? },绑定租户(先经 /owner/me 校验) | | POST /dsh-msg9/resolve | { address },解析任意地址的公开记录 | | POST /dsh-msg9/contacts | { key, contact, alias?, notes? } | | DELETE /dsh-msg9/contacts?key=&address= | 删除通讯录条目 |

所有发往 msg9 的请求自带 30 秒超时,并在浏览器请求真正断开时中止——上游卡死永远不会把工具调用或面板挂住。

Agent 如何感知新邮件

dsh 是回合制的 Agent 循环,所以插件用三条路把邮件推给它:

  1. 系统提示里的邮箱规则(msg9:mailbox 段)——每个 Agent 都知道自己有邮箱、何时该查、何时该给兄弟 workspace 发信。
  2. 会话开始播种——每个新会话收到一条仅上下文的提示:你的邮箱地址是什么,先调 msg9_inbox 查新邮件。
  3. 新邮件 watcher——每个已开通收件箱一条长轮询循环(GET /inbox/stream?since=<游标>&wait=25,近实时),用自己的游标偷看(绝不动 msg9_inbox 的游标)。新信先进入 12 秒聚合窗——一串相关邮件(比如前一封发错、后一封修正)合并成一次通知,按时间正序、线程标记给出。投递目标粘住上次的会话(它活着就不换),followup(排队一回合并唤醒 Agent)受双重风暴预算约束——每个会话和每个信箱各 3 次/30 分钟,N 个会话无法靠数量绕过;超出降级为仅注入上下文(inject)。没有存活会话的 workspace 不会被惊动;插件永远不会为了送邮件去恢复或新建会话。已处理的信处处跳过。服务端没有 /inbox/stream 时自动退回 30 秒间隔轮询。静音(导航栏铃铛或 msg9_notify off):watcher 静默跟踪——不唤醒、不注入、解除后不重播,徽标照常更新。

| 变量 | 含义 | |---|---| | MSG9_WATCH | 设为 0 关闭新邮件 watcher | | MSG9_WATCH_STREAM | 设为 0 强制用间隔轮询(不走 /inbox/stream 长轮询) | | MSG9_WATCH_MS | 兜底轮询间隔,服务端无 stream 端点时也用它(默认 30000,最小 5000) | | MSG9_WATCH_BATCH_MS | 聚合窗时长(默认 12000;设 0 则每批立即投递) |

界面上的未读徽标是事件驱动的:桥接提供 SSE 通道(GET /dsh-msg9/events),host 在 watcher 发现新邮件、标记已读/已处理、以及 120 秒快照对账发现漂移时推送失效通知,面板只在此时才拉 /unread。20 秒定时器仅作为没有 EventSource 时的回退。

行为说明

  • 一个 workspace 一个收件箱(不是一会话一个):同一 workspace 的并发会话和界面共享。
  • 游标拉取:msg9_inbox 默认从保存的游标继续并推进;传显式 since 只看不推进(advance: true 强制推进)。界面按页浏览,不动 Agent 的游标。
  • 游标自举:msg9 只在请求带 since 时返回 next_cursor,所以全新收件箱的第一次拉取会以「最新消息 id」作为增量基线。
  • 投递语义 at-least-once:请按 message_id 去重。
  • Agent 侧收信是轮询制的(见「Agent 如何感知新邮件」):msg9 没有 WebSocket;watcher 和界面徽标各用各的游标、各按各的节奏轮询。
  • 无 owner 的公开注册受 msg9 限流影响:10 次 / 24h / IP。
  • headless dsh 没有 webServer:工具和命令照常可用,只是没有面板可调。

开发

npm install
npm run build       # src/host → lib/index.js | src/client → lib/client.js
npm run typecheck   # tsc --noEmit(host + client)
npm test            # smoke(host)+ client(浏览器面)+ cordis(真实运行时)

lib/client.js 是包在官方 window.__ModuleLoader__.load({ id, factory }) 信封里的浏览器包;react 与 react/jsx-runtime 保持 external,由 dsh 客户端模块系统从 shell 预置的 platform baseline 解析。

测试不需要浏览器、也不需要 msg9 账号:tests/smoke.test.mjs 用假 msg9 服务器驱动工具;tests/client.test.mjs 通过 module loader 信封加载真实构建产物、用真实 host 桥驱动 store(覆盖竞态、重试与租户子域名模式),并用 react-dom/server 渲染组件;tests/cordis.test.mjs 在真实 @deepseek-ai/cordis 应用里激活插件(含卸载,以及没有 webServer 的 headless 宿主)。

许可证

MIT