Bundle
dsh-localnotify
DSH 本地通知栏:侧边栏【通知】入口 + 全屏通知中心(卡片列表/级别视觉分级/来源筛选/排序/时间筛选/标题内容搜索/未读已读/删除/详情弹层一键复制),notify_add agent 工具与 dsh-localnotify CLI 写入,JSON 文件持久化(支持自动清理),页面实时刷新。
- Source
- yakoylp
- License
- MIT
- Updated
- Updated 2 days ago
Readme
# dsh-localnotify
> **🌐 English**: [README.en.md](README.en.md) · **🤖 AI Agent 使用手册**: [README.agent.md](README.agent.md)



DSH(DeepSeek Harness)本地通知栏插件:在 Web UI 侧边栏新增【通知】入口,点击打开全屏**通知中心**。通知以卡片形式展示(标题 + 正文单行预览),支持**级别视觉分级**、时间/来源筛选、标题/内容搜索、排序、未读/已读标记、删除;点击卡片弹出**完整信息详情页**并可**一键复制**标题/正文。通知由 **agent 工具**(`notify_add`)或 **CLI**(`dsh-localnotify`)写入本地 JSON 文件,页面**实时刷新**,全程本地、无外部服务。
> 典型场景:任务完成后由 agent 写一条通知提醒你,你在通知栏随时回看;脚本/定时任务也可通过 CLI 直接投递。
## 功能特性
- **侧边栏【通知】入口**:铃铛图标 + 未读数量徽标(折叠态红点角标),位于左侧栏底部 Settings 旁,不遮挡现有 UI
- **全屏通知中心**(`shell.overlay`,独立于任何会话):
- 卡片列表:标题 + 正文**单行预览**(超长省略号),未读卡片带级别色左边框与圆点,高度紧凑
- **级别视觉分级**:`info`(品牌色) / `success`(绿) / `warn`(黄) / `error`(红),卡片与详情页显示级别标签,未读扫一眼即知轻重缓急
- **分页浏览**:底部工具条「每页 10/20/50/100」(默认 20)+ 上一页/下一页翻页(显示 `共 N 条 · 第 x/y 页`),只渲染当前页卡片
- **时间筛选**:全部 / 今天 / 近7天 / 近30天
- **来源筛选**:按通知来源(agent / cron / cli 等,自动汇总)过滤
- **排序切换**:最新优先 / 最旧优先 / 未读优先
- **标题 + 内容搜索**:实时过滤(不区分大小写)
- **详情弹层**:点击卡片弹出完整信息(完整时间 / 级别 / 来源 / 已读状态 + 全文),右下角**一键复制**标题、正文或全部(点击后按钮显示「已复制 ✓」),打开详情时**自动标记已读**
- 顶部「全部已读」;卡片「删除」带两步确认(防误删)
- 点关闭按钮或遮罩空白处退出
- **实时刷新**:页面打开时每 3 秒轮询,关闭时每 30 秒保持徽标同步(agent/CLI 写入后几秒内可见)
- **主题适配**:全部使用 DSH 官方主题 token(`--dsw-alias-*`),明暗主题自动跟随
- **多语言**:界面文案跟随 dsh web 语言自动切换(zh / en,切换实时生效)
- **自动清理**:可选设置保留天数,超期通知随每次写入自动删除(默认关闭,500 条上限始终兜底)
- **写入方式**(无页面新增入口,保持界面纯净):
- **agent 工具**:对话中由 agent 调用 `notify_add` 写通知(支持 `level` 分级)
- **CLI**:`dsh-localnotify add "标题" -b "正文" [--level success]`,适合脚本/定时任务
## 数据存储
通知保存在 **`~/.dsh/notify/notifications.json`**(`DSH_HOME` 环境变量优先),结构:
```json
{
"version": 2,
"notifications": [
{
"id": "n_mtisij3d_q32zcn",
"title": "任务完成",
"body": "转换结果已生成,共 23 页。",
"createdAt": 1788274582825,
"read": false,
"source": "agent",
"level": "success"
}
],
"settings": { "retainDays": 0 }
}
```
- `level`: `info | success | warn | error`(旧数据无此字段按 info 显示)
- `settings.retainDays`: 自动清理保留天数;`0`=永久保留(默认);`>0` 时每次写入自动删除超过该天数的通知(可经 CLI `config --retain-days` 设置,版本 1 旧文件读入后自动升级到 version 2)
- 写入为**原子替换**(临时文件 + rename),不会出现半截文件
- 文件**缺失或损坏时按空库处理**,绝不覆盖原文件
- 单进程内写操作串行化;最多保留 500 条,超出自动裁剪
- 文件透明可读,可手动编辑、备份、随 `~/.dsh` 整体迁移
## 安装
### 方式一:GitHub 仓库(推荐)
```sh
dsh plugin --profile web add github:yakoylp/dsh-localnotify
```
### 方式二:本地目录开发安装
先把仓库克隆(或解压源码包)到任意目录,再用该目录安装:
```sh
git clone https://github.com/yakoylp/dsh-localnotify.git
dsh plugin --profile web add ./dsh-localnotify # macOS / Linux
dsh plugin --profile web add "C:\path\to\dsh-localnotify" # Windows(换成实际路径)
```
安装后**重启 dsh web**(客户端 bundle 由 web 服务在启动时加载)。重启后侧边栏底部出现 🔔【通知】入口即为成功;可用 `dsh-localnotify add "测试"` 投递一条验证。
## CLI 用法
```sh
dsh-localnotify add "任务完成" -b "转换结果已生成" # 新增通知
dsh-localnotify add "提醒" --source cron --level warn # 指定来源与级别
dsh-localnotify add "部署成功" --level success # 级别: info/success/warn/error
dsh-localnotify list # 列出(* 为未读; [E]/[W]/[OK] 为级别标记)
dsh-localnotify list --unread # 仅未读
dsh-localnotify list --level error # 仅错误级
dsh-localnotify list --json # JSON 输出(脚本友好)
dsh-localnotify read n_xxxx # 标记单条已读
dsh-localnotify read --all # 全部已读
dsh-localnotify delete n_xxxx # 删除
dsh-localnotify config [--retain-days 7] # 查看/设置自动清理保留天数(0=永久)
dsh-localnotify prune # 按 retain-days 立即清理一次
dsh-localnotify --help # 帮助
dsh-localnotify --file /path/to/notifications.json add ... # 覆盖存储路径
```
CLI 直接读写同一个 JSON 文件,**不依赖 cordis 运行**,可在任何终端/脚本/cron 中使用。
## Agent 工具
插件注册 `notify_add` 工具,agent 在对话中调用即可写入通知(自动校验:标题必填 ≤200 字、正文 ≤5000 字)。例如任务完成时,agent 会调用:
```
notify_add(title: "文档转换完成", body: "23 页扫描件已转为 Markdown,输出于 ...", level: "success")
```
`level` 为可选枚举 `info | success | warn | error`,用于视觉分级:任务成功用 `success`、失败/出错用 `error`、需要用户注意用 `warn`、一般消息省略(默认 `info`)。
## 通知提交路径(agent / 脚本如何投递)
| 方式 | 使用者 | 前提 |
| --- | --- | --- |
| `notify_add` 工具 | DSH agent(同一 profile) | 插件已安装即自动注册;agent 在工具列表中看到该工具并按其描述在任务完成等场景调用,无需额外配置 |
| `dsh-localnotify` CLI | 脚本 / cron / 终端 | 插件已安装(bin 链接到 profile 的 `node_modules/.bin`);或直接 `node <插件路径>/lib/cli.js` |
| 直接写 JSON 文件 | 任意程序 | 按上文文件格式写入 `~/.dsh/notify/notifications.json`,页面 3s 内可见 |
> 说明:`notify_add` 工具对**安装了插件的同一 DSH 环境(profile)**内的 agent 可见;其他环境/agent 需各自安装插件才能用工具投递,但 CLI 与文件写入不受环境限制。
## HTTP API
Host 半区注册同源路由 `POST /dsh-localnotify/api/<method>`(仅 DSH 自带 web 服务可达),请求/响应均为 JSON,响应包裹 `{ ok, value }` 或 `{ ok: false, error: { code, message } }`:
| 方法 | 参数 | 说明 |
| --- | --- | --- |
| `list` | `{}` | 返回 `{ notifications, storagePath }` |
| `markRead` | `{ id }` | 标记单条已读 |
| `markAllRead` | `{}` | 全部已读 |
| `delete` | `{ id }` | 删除单条 |
## 架构
- **Node 半区**(`cordis.patch.yml` 挂载,`lib/index.js`):注册 `notify_add` 工具(`ctx.tools.register`)与 `/dsh-localnotify/api/*` 路由(`ctx.webServer.register`,prefix 匹配);存储逻辑在 `lib/store.js`(Host 与 CLI 共用)
- **浏览器半区**(`dsh.client` + `exports["./client"]` → `lib/client.js`):手工编写的 ModuleLoader bundle(`window.__ModuleLoader__.load({ id, factory })`,与官方 tsdown 产物同格式,仅依赖基线外部模块 `react`);通过 `ctx.slots.inject/register` 注册 `sidebar.footer.action`(入口)与 `shell.overlay`(通知中心),数据走同源 fetch 调 Host API
- **主题**:颜色全部引用 DSH 主题 token(`--dsw-alias-bg-*` / `--dsw-alias-label-*` / `--dsw-alias-border-*` / `--dsw-alias-brand-primary` 等)
## 开发
```sh
node --test # 运行全部测试(store 存储层 + API handler + CLI 端到端)
node lib/cli.js --file ./tmp-notify.json add "测试" -b "正文" --level success
node lib/cli.js --file ./tmp-notify.json list
node lib/cli.js --file ./tmp-notify.json config --retain-days 7
```
## 常见问题(排障)
- **侧边栏入口可见,但通知中心列表为空**:先直测 Host 路由是否注册——`curl -X POST http://127.0.0.1:<port>/dsh-localnotify/api/list -H "Content-Type: application/json" -d "{}"`。若返回 **405**(空 body),说明 Host 路由未注册,请升级到 **≥1.1.0**(1.0.0 存在 `inject` 缺 `webServer` 导致路由静默失效的问题);若返回 `{ ok:true, value: { notifications, ... } }` 则路由正常,检查存储文件是否有数据
- **多条通知被压成细条、无滚动条**:1.0.0 的 flex 布局缺陷,升级到 **≥1.1.0**(卡片已禁止 flex 压缩,列表容器可正常滚动)
## 已知限制与后续
- 通知中心为全屏浮层(非独立路由页面),关闭后回到原会话 —— 设计取舍:保持轻量,不占导航
- 实时性依赖轮询(打开 3s / 关闭 30s),未做 Host→Client 推送。轮询成本极低(同源小请求),且不依赖额外事件通道;如未来 DSH 提供推送通道再评估升级
- 多进程同时写(如 CLI 与 Host 并发)依赖原子 rename,极端并发下后写者胜出。通知场景写入频率低,该语义足够安全(单进程内写已串行化);若未来出现高频并发写入需求再引入文件锁
- 文件被外部工具修改时,以读取时内容为准(页面 3s 内可见)
## 测试与 CI
- 测试覆盖三层:存储层(新增/校验/级别/自动清理/旧文件兼容/损坏兜底/500 上限)、HTTP API(方法分发/错误包裹/读写联动)、CLI(spawn 端到端:add/list/read/delete/config/prune/错误路径)
- GitHub Actions 在 Node 18 / 20 / 22 上自动运行全部测试(`.github/workflows/test.yml`)
## License
MIT
Install
dsh plugin --profile web add github:yakoylp/dsh-localnotify
Profile: web
With the hub plugin installed, ask your agent to install it by name — it resolves the same plan shown here.
dsh plugin --profile web add github:stvlynn/dsh.fish#path:packages/dsh-plugin-hub
install dsh-localnotify from the hub
- This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.