docs(example): 为每个插件补 README

此前 example/ 下 21 个插件里,13 个完全没有 README,另 4 个是
`hmapdev init` 生成的脚手架样板(`# <name>` + `plugin build` + `Install` 三行,
等于从没被写过)。只有 deepsearch / vikunja / plugindev / luademo 是真实文档。

本次为 **17 个**插件写了真文档(13 个缺失 + 4 个样板),现在 21 个全部有内容。

## 写法

每个 README 覆盖:能力一句话 → 为什么需要 → 工具表 → 配置项表 →
通道与钩子(有才写)→ 构建 → 已知边界。

**事实全部从源码读出来,不推测**:
- 工具名核对到注册点(含 `tp+"x"` / `p.name+"_x"` 前缀拼接,展开成最终名)
- 配置键与默认值取自 `RegisterDef` / getStr 默认值
- 通道名、钩子名、依赖命令逐条 grep 确认
- 版本号与已部署实例交叉核对,17 个里 16 个一致

## 几处按源码写、与直觉不同的点

- **rss**:订阅时会把抓到的历史条目一次性标为 seen,所以订阅一个源
  **不会**把历史文章全推一遍 —— 这是避免刷屏的关键,写进了文档。
- **files**:路径校验是**两道**(规范化后判断 + 解析符号链接后再判断),
  只做前者的话沙箱里的软链接就能逃逸。两种情况报错文案不同。
- **qq**:身份必须**绑帧**而非存插件全局,源码注释记录了由此产生的两个真实故障
  (中断抢占恢复后权限门整体失效、运行中到达的消息改写正在跑那一轮的身份)。
  多来源合并时权限取**交集**。硬私有工具按前缀一律拒绝。这些是安全关键,
  单独成节写清楚。
- **memo**:待办与备忘录**刻意分两类**(一提醒一不提醒),提醒注入带 NoMemory。
- **sanitizer**:不注册任何工具,只挂三个阶段钩子;依赖 ABI v2 的 stage 写回能力。
- **editdoc**:本目录是 v1.0.0(单工具),而线上跑 v2.0.0(全能版,源码未公开)——
  在文档开头显式标注,**不按 v2 描述**,避免读者以为这里就是线上那份。

## 验证

- 21/21 文件非空且非样板(最小 913B,最大 6845B)
- 逐个核对 README 中出现的工具名能在源码找到依据;5 处报警经复核**全是误报**
  (`ai_image_generate`/`music_*` 前缀来自 metadata 的 name,`on_input` 等是钩子不是工具)
- README 版本号 vs 线上 plugin.json:16/17 一致,editdoc 的差异已显式说明

注:本仓既有未提交改动(example/qq/plugin.go、sdk/plugin.go)**未纳入本次提交**。
This commit is contained in:
JianFeeeee
2026-09-20 00:43:06 +08:00
parent cfa72df3e9
commit 7ef9bc2ad3
17 changed files with 901 additions and 27 deletions

47
example/a2a/README.md Normal file
View File

@ -0,0 +1,47 @@
# a2a · Agent-to-Agent 通信
让本 Agent 与其他 Agent **双向互调**:既能对外暴露自己的能力,也能去问别的 Agent。
## 两个方向
| 方向 | 怎么实现 |
|---|---|
| **入站**(别人问我) | 插件起一个 HTTP 服务端,暴露 `/agent-card`(能力描述)与 `/a2a`JSON-RPC 入口) |
| **出站**(我问别人) | 提供 `a2a_query` / `a2a_discover` 工具,主动向远端 A2A Agent 发起请求 |
## HTTP 端点
| 路径 | 作用 |
|---|---|
| `GET /agent-card` | 返回 Agent Card本 Agent 的能力描述,供对方发现 |
| `POST /a2a` | JSON-RPC 2.0 入口,接收对方的任务请求 |
## 工具
| 工具 | 说明 |
|---|---|
| `a2a_a2a_query` | 向另一个 A2A Agent 发查询并取回复 |
| `a2a_a2a_discover` | 取对方的 Agent Card能力描述 |
| `a2a_a2a_status` | 看本插件运行状态(监听地址、当前配置) |
| `a2a_a2a_configure` | 改配置并自动重启服务(可动态改监听地址) |
| `a2a_a2a_restart` | 重启 HTTP 服务端(连接异常或改配置后用) |
> 工具名前缀取自插件名(`tp`),按默认 `a2a_` 列出。
## 配置项
| 键 | 默认 | 说明 |
|---|---|---|
| `listen` | `127.0.0.1:12000` | 服务端监听地址。**设为空可禁用 HTTP 服务**(只出站、不入站) |
## 典型用法
1. **先发现再调用**`a2a_discover` 拿对方能力 → 决定要不要发、发什么 → `a2a_query`
跳过 discovery 直接问,容易问出对方不支持的东西。
2. **只出站**:把 `listen` 设为空,本 Agent 不外露端口,但仍能主动联系别人。
## 构建
```bash
hmapdev build
```

51
example/acp/README.md Normal file
View File

@ -0,0 +1,51 @@
# acp · Agent Client Protocol 通信
[ACP](https://agentclientprotocol.com/) 桥接:本 Agent 既能**当服务端**接别人的任务,也能**当客户端**去调别的 ACP Agent。
## 两个方向
| 角色 | 行为 |
|---|---|
| **服务端** | 在本机起 HTTP 服务,处理 `session/new` / `session/update`,接受其他 Agent 的任务请求 |
| **客户端** | 通过 `acp_query` 向远程 ACP Agent 发 `session/new` 并读回复 |
## 协议端点
- `POST /api/session` —— JSON-RPC支持 `session/new``session/update`
- 客户端侧同时兼容**两种服务端**SSE 型(流式 `session/reply`)与同步 JSON 型
## 工具
| 工具 | 说明 |
|---|---|
| `acp_acp_query` | 向远程 ACP Agent 发起会话并等待回复,返回最终回答文本 |
| `acp_acp_status` | 查看运行状态与**当前活跃会话数** |
| `acp_acp_configure` | 改监听配置并重启 HTTP 服务 |
> 工具名前缀取自插件名(`tp`),按默认 `acp_` 列出。
`acp_query` 可指向的远端举例(源码注释给的):
- opencode`http://127.0.0.1:13000`
- pi bridge`http://127.0.0.1:12011`
- 回环到自身:`http://127.0.0.1:12001`
## 配置项
| 键 | 默认 | 说明 |
|---|---|---|
| `listen` | `127.0.0.1:12001` | 服务端监听地址。**设为空可禁用 HTTP 服务**(只出站) |
## 与 a2a 的区别
| | a2a | acp |
|---|---|---|
| 面向 | Agent ↔ Agent 对等通信 | 客户端 → Agent 会话(每次一个 session |
| 会话 | 一问一答 | 有 session 生命周期,可续 |
| 发现 | `/agent-card` | 无(需已知地址) |
## 构建
```bash
hmapdev build
```

View File

@ -1,13 +1,34 @@
# ai_image
# ai_image · 文生图
ai_image plugin
按文字提示生成图片,下载到本地并返回**文件路径**。
## Build
## 工具
| 工具 | 说明 |
|---|---|
| `ai_image_generate` | 按 prompt 生成图片 |
返回值是**本地文件路径**(永久,不过期)。要把图给用户看,再用导出的通道
`type=image``payload=<该路径>` 发送。
## 配置项
| 键 | 默认 | 说明 |
|---|---|---|
| `api_key` | 空 | OpenAI / Stable Diffusion 的 API Key |
| `base_url` | 空 | 自定义 OpenAI 兼容网关(**不带 `/v1` 尾缀**,如 `http://127.0.0.1:8081`)。留空走官方 `https://api.openai.com` |
| `provider` | `openai` | 服务方:`openai` / `stability` |
| `model` | `dall-e-3` | 模型名(如 `dall-e-3``sd-xl` |
| `size` | `1024x1024` | 默认尺寸,也可 `1024x1792` / `1792x1024` |
## 实现要点
- **返回本地路径而不是远端 URL**:远端图床链接会过期,写进记忆就成了悬空指针。
下载到本地后路径稳定,可交给媒体存储做内容寻址。
- 配了 `base_url` 就能指向自建/兼容网关,不必依赖官方接口。
## 构建
```bash
hmapdev build
```
## Install
Upload the .hmap file through the Plugin Manager API.

43
example/bili/README.md Normal file
View File

@ -0,0 +1,43 @@
# bili · B站视频下载
用 [yt-dlp](https://github.com/yt-dlp/yt-dlp) 把 B 站视频下载到本地。
## 前置依赖
需要系统里装有 `yt-dlp`
```bash
pip install -U yt-dlp # 或 apt install yt-dlp
```
## 工具
| 工具 | 说明 |
|---|---|
| `bili_video` | 下载 B 站视频;不指定 `format` 时先返回可用清晰度列表,指定后真正下载并返回文件路径 |
参数:
| 参数 | 说明 |
|---|---|
| `url` | 视频地址 |
| `format` | 格式 ID。常用`30112`/`30080`=1080P、`30064`=720P、`30032`=480P、`30016`=360P。不指定则自动选最优 |
## 配置项
| 键 | 默认 | 说明 |
|---|---|---|
| `output_dir` | `/tmp/bili_videos` | 下载目录 |
| `proxy` | 空 | yt-dlp 使用的 HTTP 代理(如 `http://127.0.0.1:7890`)。留空则不设代理 |
## 实现要点
- **`output_dir` 有安全校验**:它是配置项,但会拒绝被配成系统目录,避免 yt-dlp 往任意位置写文件。
- 两阶段用法:先不传 `format` 拿到清晰度清单(`format_id` + `format_note`),再带上选定的 ID 下载。这样模型不会盲选一个不存在的格式。
- B 站在部分网络环境下需要代理,见上面的 `proxy`
## 构建
```bash
hmapdev build
```

66
example/browser/README.md Normal file
View File

@ -0,0 +1,66 @@
# browser · 统一浏览器
一个插件覆盖三种"访问网页"的能力,从最轻到最重。**按需选层**是这个插件的重点 ——
绝大多数抓取用 HTTP 就够,不该为了一句话启动 Chromium。
## 三种能力层
| 层 | 工具 | 何时用 |
|---|---|---|
| **搜索** | `browser_search` | 要的是"找到哪些页面",不是页面本身 |
| **quick纯 HTTP** | `browser_fetch``mode=quick` | 静态页、API、能直接拿到 HTML |
| **normal无头渲染** | `browser_render` / `browser_fetch``mode=render` | JS 渲染的页面HTTP 拿不到内容 |
| **interactiveCDP** | `browser_start` + `navigate`/`click`/`type`/`scroll`/`html`/`screenshot` | 需要交互:登录、点按、翻页 |
`browser_fetch``mode`
- `auto`(默认):先试 HTTP**遇 403/429 才降级**用 Chromium 渲染
- `render`:强制 Chromium
- `quick`:纯 HTTP不降级
## 工具
| 工具 | 说明 |
|---|---|
| `browser_search` | 网页搜索 |
| `browser_fetch` | 抓取 URL 内容,三种 mode 见上 |
| `browser_render` | 无头 Chromium 渲染并提取文本normal |
| `browser_start` | 启动交互式浏览器会话CDP |
| `browser_navigate` | 导航到指定 URL |
| `browser_click` | 点击元素 |
| `browser_type` | 输入文本 |
| `browser_scroll` | 滚动页面 |
| `browser_html` | 取当前页 HTML |
| `browser_screenshot` | 截图 |
| `browser_install` | 安装 systemd 托管的共享浏览器后端 |
| `browser_close` | 关闭会话 |
## 共享浏览器后端
`browser_install` 安装 `homeagent-browser.service`systemd 托管)。
装上之后**所有 agent 共享同一个 Chromium 实例与登录态**,各自占独立标签页互不干扰
(同 source 复用自己的标签页)。
前提:本机已有 chromium 二进制,没有会提示先装(`apt install chromium` 或等价)。
## 实现要点
- **搜索用 `cn.bing.com` 而不是 `www.bing.com`**:后者对程序化请求常回 302同意/重定向页),
根本拿不到结果块。
- **标题取 `<h2>` 里的 `<a>`**:直接抓结果块里第一个 `<a>` 会拿到来源行而非标题。
- **摘要认 `b_lineclamp`**:旧版 Bing 用 `b_caption`,新版已迁走,两套都匹配。
- **有 SSRF 防护**:见源码 `SSRF` 段,抓取前校验目标地址,避免被诱导访问内网。
## 测试
```bash
go test -count=1 ./...
```
`testdata/bing_cn.html` 是搜索解析的固定样本,用它做离线断言,避免测试依赖真实网络。
## 构建
```bash
hmapdev build
```

View File

@ -1,13 +1,57 @@
# calendar
# calendar · 日历事件
calendar plugin
事件管理:支持**重复事件**与**多档提醒**。
## Build
## 工具
| 工具 | 说明 |
|---|---|
| `calendar_event_add` | 添加事件 |
| `calendar_event_list` | 列出即将到来的事件(含日期、时间、重复规则) |
| `calendar_event_update` | 更新事件(**只改传入的字段**;会重置提醒状态) |
| `calendar_event_delete` | 删除事件(连带**该事件及之后的所有重复实例** |
| `calendar_today` | 今日事件 + 倒计时 |
| `calendar_week` | 本周事件,按天分组 |
| `calendar_month` | 月历网格,带事件标记点 |
| `calendar_search` | 按关键词搜标题 / 地点 / 备注 |
`calendar_event_add` 的时间格式:`YYYY-MM-DD HH:MM`;只给 `YYYY-MM-DD` 表示全天事件。
## 重复规则
`repeat` 取值:
| 值 | 含义 |
|---|---|
| `none` | 不重复 |
| `daily` | 每天 |
| `weekday` | 每个工作日 |
| `weekly` | 每周 |
| `biweekly` | 每两周 |
| `monthly` | 每月 |
| `yearly` | 每年 |
| `lunar_yearly` | **按农历年**(生日、传统节日用) |
`lunar_yearly` 是刻意加的:农历节日按公历写死会逐年偏移。
## 提醒
`remind_before` 单位是**分钟**,可给多个、逗号分隔:
```
15,60,1440 # 提前 15 分钟 + 1 小时 + 1 天
0 或留空 # 不提醒
```
到点通过 `InjectInterruptText` 注入提醒,带 `NoMemory: true` ——
提醒是瞬时信号,不是记忆内容。通道 `calendar` 同样声明为 NoMemory。
## 存储
事件存为 JSON插件重启后保留。
## 构建
```bash
hmapdev build
```
## Install
Upload the .hmap file through the Plugin Manager API.

49
example/editdoc/README.md Normal file
View File

@ -0,0 +1,49 @@
# editdoc · Office 文档编辑
编辑 `.docx` / `.xlsx` / `.pptx` 内容:查找替换、改单元格、插行。
> ⚠️ **版本说明**:本目录是 **v1.0.0**,只有 `edit_document` 一个工具。
> 线上部署的 v2.0.0(全能办公版,支持新建/读取/转换 docx·xlsx·pptx·md·csv·txt
> **源码尚未公开**,本文档不描述那些能力。参见 `plugin.json` 的 `version`。
## 工具
| 工具 | 说明 |
|---|---|
| `edit_document` | 编辑文档内容,**编辑后原文件被覆盖** |
参数:
| 参数 | 说明 |
|---|---|
| `file` | 文档路径(必填) |
| `operation` | `replace_text`(查找替换)/ `set_cell`(设置单元格)/ `insert_row`(插入行)(必填) |
| `target` | 要查找的文本(`replace_text` 用) |
| `replacement` | 替换为的文本(`replace_text` 用) |
| `sheet` | 工作表名xlsx 可选) |
| `row` | 行号(`set_cell` / `insert_row` 用) |
| `col` | 列号(`set_cell` 用) |
| `value` | 单元格值(`set_cell` 用) |
编辑前建议先读一遍内容确认目标文本 —— 查找替换是**全文件覆盖写**,没有撤销。
## 配置项
| 键 | 默认 | 说明 |
|---|---|---|
| `script_path` | 空 | `edit_doc.py` 的绝对路径。留空则用插件可执行文件同目录下的 `edit_doc.py` |
| `venv_python` | 空 | 执行 `edit_doc.py` 的 Python 解释器(建议用 venv 里的)。**必须配置,留空会报错** |
## 工作原理
本插件是 Go 写的薄壳:把参数序列化成 JSON交给 Python 脚本 `edit_doc.py` 执行实际文档操作。
文档解析依赖 Python 侧的库python-docx / openpyxl / python-pptx 之类),所以:
- **需要自备 `edit_doc.py`**:它不在本目录里。
-`venv_python` 指向装了这些库的解释器,避免污染系统 Python。
## 构建
```bash
hmapdev build
```

44
example/files/README.md Normal file
View File

@ -0,0 +1,44 @@
# files · 沙箱文件操作
读写与编辑文件,**全部操作限制在沙箱目录内**。
## 工具
| 工具 | 说明 |
|---|---|
| `files_read` | 读文件内容,支持 `offset` / `limit` 读大文件 |
| `files_write` | 写文件,**自动创建父目录** |
| `files_edit` | 按精确字符串替换改文件 |
| `files_ls` | 列目录(目录名带 `/` 后缀) |
`files_edit``edits[]` 传多组替换,每组 `{old, new}`
- 每个 `old` 必须在**原文件**中**恰好出现一次** —— 不唯一会报错,避免改错地方。
- 所有替换都针对**原内容**匹配,不要在同一个 `edits` 里写相互重叠的改动。
## 配置项
| 键 | 默认 | 说明 |
|---|---|---|
| `dir` | 空 | 允许访问的根目录。留空用默认沙箱(主数据目录下的 `files_sandbox`)。**不建议设为 `/`** |
## 沙箱实现
路径校验不止一次,是两道:
1. **规范化后判断**`filepath.Abs` + `filepath.Clean`,再用 `withinSandbox`
检查结果是否在根目录之下(`/` 作为特例放行)。
2. **解析符号链接后再判断**`filepath.EvalSymlinks` 求出真实路径,**再查一次**沙箱。
第 2 步是关键:只做第 1 步的话,沙箱内一个指向外部的软链接就能绕过限制
`.../sandbox/link -> /etc`)。报错文案也区分了这两种情况
`path outside sandbox` vs `path escapes sandbox via symlink`)。
对不存在的路径(`write` 会用到),求真实路径时只对已存在的部分做 `EvalSymlinks`
其余保留为未创建的尾部。
## 构建
```bash
hmapdev build
```

43
example/memo/README.md Normal file
View File

@ -0,0 +1,43 @@
# memo · 待办与备忘录
两类条目,行为**刻意不同**
| 类型 | 用途 | 是否主动提醒 |
|---|---|---|
| **待办**todo | 有截止概念、需要被催的事 | ✅ 会 |
| **备忘录**memo | 纯记事,供以后查阅 | ❌ 不会 |
分开的理由:把"提醒我"和"记一下"混成一类,要么备忘录天天弹、要么待办被忘掉。
## 工具
| 工具 | 说明 |
|---|---|
| `memo_todo_add` | 添加待办(会被主动提醒) |
| `memo_todo_complete` | 标记待办完成(不再提醒) |
| `memo_todo_list` | 列出未完成待办(含 ID、内容、创建时间 |
| `memo_todo_delete` | 删除待办(含已完成的) |
| `memo_memo_create` | 创建备忘录(纯记事,不提醒) |
| `memo_memo_list` | 列出全部备忘录 |
| `memo_memo_delete` | 删除备忘录 |
> 工具名前缀取自插件名(`p.tp`),上面按默认的 `memo_` 写法列出。
## 提醒机制
- 后台 **每 5 分钟**检查一次未完成待办数;有则通过 `InjectInterruptText` 注入一条
「注意,你还有 N 条待办未完成,请检查」。
- 注入带 **`NoMemory: true`** —— 这是定时提醒,不是记忆内容,不该进向量化。
- 通道声明为 **`NoMemory`**`RegisterInputChannel(p.name, ChannelDef{NoMemory:true})`
理由同上:提醒是瞬时信号。
- 另有 `StagePreAction` 钩子,在每轮动作前参与。
## 存储
条目落在数据目录的 `todos.json`,插件重启后仍在。
## 构建
```bash
hmapdev build
```

30
example/music/README.md Normal file
View File

@ -0,0 +1,30 @@
# music · 音乐搜索
按关键词搜歌、按 ID 查歌词(数据来自网易云音乐公开接口)。
## 工具
| 工具 | 说明 |
|---|---|
| `music_search` | 按关键词搜歌,返回歌曲列表(含歌曲 ID |
| `music_lyrics` | 按歌曲 ID 取歌词 |
典型两段式用法:先 `music_search` 拿 ID`music_lyrics` 取词。
## 实现要点
- 请求打的是 `https://music.163.com/api/...`,并固定带上 `Referer: https://music.163.com/` ——
该接口对缺少来源头的请求会拒绝。
- 是**只读**插件:不下载音频、不写本地文件,因此没有需要清理的副作用。
## 已知边界
- 依赖第三方(网易云)公开接口,其可用性与返回结构不受本插件控制;
接口变动时可能返回空列表,而不是报错。
- 仅覆盖"搜索 + 歌词",不含播放地址解析。
## 构建
```bash
hmapdev build
```

40
example/ocr/README.md Normal file
View File

@ -0,0 +1,40 @@
# ocr · 图片文字识别
从图片里提取文字(中英文),基于 [Tesseract](https://github.com/tesseract-ocr/tesseract) OCR 引擎。
## 前置依赖
需要系统里装有 `tesseract` 可执行文件:
```bash
# Debian/Ubuntu
apt install tesseract-ocr tesseract-ocr-chi-sim
```
中文识别需要 `chi_sim` 语言包;缺它时中文会识别成乱码而非报错。
## 工具
| 工具 | 说明 |
|---|---|
| `ocr_ocr_image` | 对图片做 OCR返回识别文本 |
参数:
| 参数 | 说明 |
|---|---|
| `image_url` | 图片的 HTTP/HTTPS 地址(与 `image_data` 二选一) |
| `image_data` | 图片的 base64 数据,**不含** `data:image/...` 前缀(与 `image_url` 二选一) |
| `language` | 识别语言,默认 `chi_sim+eng`;可选 `chi_sim` / `eng` / `chi_sim+eng` |
## 实现要点
- 传入的图先落到临时目录OCR 完 `defer os.RemoveAll` 清掉,不残留。
- 调用参数固定 `--psm 3`(全自动页面分割),适合截图与常规排版图片;对单行小图或竖排文本效果会下降。
- **`Cleaner`**:工具返回的是 JSON`text``language` 等字段),进记忆计算前只取 `text` 正文 —— 否则 JSON 结构本身会参与向量化。
## 构建
```bash
hmapdev build
```

167
example/qq/README.md Normal file
View File

@ -0,0 +1,167 @@
# qq · QQ 消息桥接
通过 [NapCat](https://github.com/NapNeko/NapCatQQ) 把 QQ 接成 HomeAgent 的一个 IO 通道:
让 agent 收发 QQ 消息、读群/好友信息、传文件。
> ⚠️ 这是**安全敏感**插件:它让外部 QQ 用户能触达 agent 的工具。
> 本文档的「权限模型」一节请务必读完。
## 通道与钩子
| 类型 | 名称 | 说明 |
|---|---|---|
| 出站 | `qq` | `CapText` + `CapFile` + `CapImage` + `CapAudio`;发消息/文件给 QQ |
| 入站 | `qq` | `NoMemory: true` + `Cleaner` + `RecallPolicy: None` |
四个阶段钩子(全部 `StageScopeGlobal`
| 钩子 | 作用 |
|---|---|
| `on_input` | 把本轮 QQ 身份**绑到帧上** |
| `before_toolcall` | 权限门:逐个工具判断是否放行 |
| `post_action` | 清掉被拒绝时模型已经吐出的废话 |
| `after_output` | 收尾时清理插件全局身份 |
## 工具20 个)
| 工具 | 说明 |
|---|---|
| `qq_get_message` | 按 `message_id` 取消息正文、发送者、附件 |
| `qq_get_history` | 取群/私聊最近历史消息 |
| `qq_list_chats` | 会话列表(按最新消息排序,带未读数与摘要) |
| `qq_mark_read` | 把某会话未读数清零 |
| `qq_send_file` | 发文件/图片(私聊或群聊) |
| `qq_get_groups` | 群列表,可按关键词搜 |
| `qq_get_friends` | 好友列表,可按昵称/备注搜 |
| `qq_get_recent_contacts` | 最近有消息的联系人与群 |
| `qq_resolve_name` / `qq_resolve_nickname` | 名字 ↔ QQ 号互查 |
| `qq_get_group_member_info` | 群成员信息 |
| `qq_group_manage` | 群综合管理(见下) |
| `qq_friend_action` | 好友操作 |
| `qq_get_group_files` | 群文件列表 |
| `qq_download_file` / `qq_upload_group_file` / `qq_get_download_tasks` | 文件传输与任务 |
| `qq_read_document` | 读 QQ 传来的文档 |
| `qq_video_download` | 下载视频 |
| `qq_send_like` | 点赞 |
`qq_group_manage` 一个工具承载多种操作(`command` 参数):
`leave` 退群、`kick` 踢人、`ban`/`unban` 禁言解禁、`rename` 改名、`mute-all` 全员禁言、
`set-card` 设名片、`set-admin` 设管理、`set-title` 设头衔、`member-list``group-info`
`msg-history``recall` 撤回、`pin-msg` 精华、`list-files``pending-requests``folder-create` 等。
**破坏性操作**`leave`/`kick`/`ban`/`unban`/`rename`/`mute-all`/`set-card`/`set-admin`/
`set-title`/`recall`/`pin-msg`/`folder-create`**必须显式传 `confirm: true`**。
## 权限模型
这是本插件最重要的部分。
### 身份分级
| 身份 | 权限 |
|---|---|
| **owner**Bot 所有者) | 私聊或群聊均**完整放行** |
| **普通 QQ 用户** | 只放行白名单内的工具 |
### 身份必须「绑帧」,不能只存插件全局
源码注释记录了两个真实故障,这就是绑帧的原因:
1. **中断抢占后身份丢失**:中断会抢占当前轮、把现场压栈。中断轮收尾时
`after_output` 会清空插件**全局**身份;随后外层被恢复(`resumeTask` 复用同一帧、
**不重跑 `on_input`**)。若身份只存全局,恢复后的外层就是"无身份"
`before_toolcall``!auth.active` 处直接返回 —— **整个权限门失效**
2. **运行中到达的消息改写身份**:新消息会调 `activateAuthContext` 改写全局身份,
把**正在跑的那一轮**换成另一方的身份(换高=越权,换低=误拒)。
帧上的 `Extra` 随帧一起压栈/恢复,正好是"这一轮的身份"。
### 合并取最小权限
多来源被内核合并到同一推理时,权限**取交集**而非并集:
```go
p.auth.owner = p.auth.owner && next.owner
```
防的是"非所有者请求 + 随后所有者消息"意外把前一个请求提权。
### 硬私有工具
非所有者**一律拒绝**(不看白名单),按前缀拦截:
`calendar_``email_``mail_``agentmail_``memory_``knowledge_``device_`
`devicectl_``terminal_``shell_``command_``exec_``filesystem_``agentfs_`
`config_``settings_``plugin_``plugins_`
外加 `read_file``write_file``edit_file``delete_file``list_files``run_command`
`homeagent_config``homeagent_restart``output_send__email``output_send__mail`
### 参数与会话一致性校验
光看工具名不够,还要检查**参数指向的会话与当前身份一致**,否则可以拿别人的
`message_id` 去读别处内容:
-`message_id` 的工具:该 ID 必须属于当前 QQ 会话(`lookupMsgRef` 校验 peer 与群/私聊类型)。
- `get_group_member_info` / `get_group_files``group_id` 必须是**当前群**。
### 频率与重复控制
| 键 | 作用 |
|---|---|
| `max_qq_tool_calls` | 单轮工具调用上限 |
| `max_qq_output_calls` | 单轮输出调用上限 |
| `max_duplicate_qq_send` | 重复发送上限,防刷屏 |
| `batch_window_ms` / `batch_max_ms` | 消息合批窗口 |
被拒时只允许**发一次权鉴说明**,之后锁止本轮剩余工具调用
`clearDeniedResponse` 再把模型已写出的内容清掉,避免输出里带一堆"我不能…")。
## 配置项
### 连接
| 键 | 默认 | 说明 |
|---|---|---|
| `napcat_url` | — | NapCat 服务地址 |
| `listen` | — | 本插件 HTTP 监听地址 |
| `webhook_token` | — | webhook 校验令牌 |
### 身份与准入
| 键 | 默认 | 说明 |
|---|---|---|
| `owner` | 空 | Bot 所有者 QQ 列表(逗号分隔),拥有完整权限 |
| `admin` | 空 | **旧配置名**`owner` 为空时作为所有者列表(兼容用) |
| `dm_policy` | `open` | 私聊策略:`open` / `allowlist` / `disabled` |
| `allow_from` | 空 | 私聊白名单QQ 号,逗号分隔) |
| `group_policy` | `open` | 群聊策略:`open` / `allowlist` / `disabled` |
| `group_allow_from` | 空 | 群白名单 |
| `private_tool_allowlist` | 空 | 私聊下非所有者可用的工具 |
| `group_tool_allowlists` | 空 | 按群配置的工具白名单 |
### 文件与转发
| 键 | 说明 |
|---|---|
| `files_dir` | 本地文件目录 |
| `remote_dir` | 供 NapCat 容器访问的目录(发文件前先复制到这里) |
| `agentfs_dir` | agent 文件系统目录 |
| `forward_rules` | JSON 数组,每项 `{group_id,host,port,password,template}`:匹配的群消息经 **RCON** 转发到 Minecraft`template` 支持 `{nickname}` / `{message}` 占位 |
## 部署前提
需要**自行部署 NapCat**(本插件不含 QQ 协议实现,只是 NapCat 的客户端)。
发文件前会先把文件复制到 `remote_dir`,因为 NapCat 通常在容器里,看不到宿主任意路径。
## 测试
```bash
go test -count=1 -race ./...
```
含权限门与绑帧的回归测试。改动权限相关代码后务必跑 `-race`
## 构建
```bash
hmapdev build
```

View File

@ -0,0 +1,53 @@
# recoverydiag · 快速检查 / 崩溃取证
给 guard 与 failback 用的**确定性诊断工具集**。
设计基调(源码原话):**返回结论而非原文,确定性检出,不消耗 LLM token。**
崩溃后最忌讳的是把几万行日志塞进模型上下文让它"看看",那既慢又不可靠 ——
这里每个工具都在本地算出结论再返回。
## 工具
| 工具 | 说明 |
|---|---|
| `recoverydiag_diag_triage` | 快速分诊:按退出码 / 信号 / 存活状态粗分类别(进程死亡 vs 配置类不可达 vs 正常) |
| `recoverydiag_diag_db` | config.db 完整性(`PRAGMA integrity_check`+ LLM 源解析校验(`core.llm.sources.*` 必备字段),逐项 ok/fail |
| `recoverydiag_diag_log_scan` | 在日志目录的时间窗内统计已知错误签名panic / OOM / 网络不可达 / provider 失败 / sql / 致命)出现次数,给出主导结论 |
| `recoverydiag_diag_delta` | 对比 baseline上次 good 快照/目录)与现状,列出 created / modified / deleted 清单与摘要,判定"改了什么" |
| `recoverydiag_diag_loc` | 综合前四项结论,按**因果强度正交排序**定位根因并给出推荐恢复动作 |
## 用法顺序
```
diag_triage → diag_db → diag_log_scan → diag_delta → diag_loc
(各自独立,可只跑需要的) (要传前四项的结论)
```
`diag_loc` 需要你把它余下的结论**作为参数传进去**`triage` / `db` / `log` / `delta` 四个对象),
它不自己去调 —— 这样它只做归因,不重复执行。
## 配置项
| 键 | 默认 | 说明 |
|---|---|---|
| `db_check_cmd` | `auto` | `diag_db` 用的 `sqlite3` 命令。留空=auto可用时用 sqlite3缺失则回退读内核 Settings |
| `recovery_kb_dir` | 空 | `diag_loc` 结论 JSON 的落盘目录。缺省 `<data_dir>/recovery_kb` |
## 不注册通道与钩子
本插件**只提供工具**,不订阅输入、不挂阶段钩子 —— 它是被 guard 或 agent 主动调用的,
不做后台干预。
## 测试
```bash
go test -count=1 ./...
```
`diag_test.go` 覆盖各诊断项的判定逻辑。
## 构建
```bash
hmapdev build
```

View File

@ -1,13 +1,44 @@
# rss
# rss · RSS/Atom 订阅监控
rss plugin
订阅 RSS/Atom 源,**有新文章时主动通知** agent不必每轮去问
## Build
## 工具
| 工具 | 说明 |
|---|---|
| `rss_subscribe` | 订阅一个 RSS/Atom 源 |
| `rss_unsubscribe` | 取消订阅 |
| `rss_list` | 列出全部订阅 |
| `rss_check_now` | 立即检查所有源(不等轮询周期) |
## 配置项
| 键 | 默认 | 说明 |
|---|---|---|
| `poll_interval` | `30` | 默认轮询间隔(**分钟** |
订阅时可对单个源覆盖间隔。
## 通知机制
- 后台按各自间隔轮询(默认 30 分钟)。
- 发现新条目时通过 `InjectInterruptText` 注入,格式形如
`📡 <源标题> (<URL>) — N 篇新文章:` 后跟条目。
- 注入带 **`NoMemory: true`**,通道 `rss` 也声明为 `NoMemory` ——
订阅推送是信号不是知识,不该进向量化挤掉别的记忆。
## 实现要点
- **订阅时就记下全部已有 GUID**`handleSubscribe` 会把抓取到的历史条目
一次性标为 `seenGUIDs`,所以**订阅一个源不会把它的历史文章全部推送一遍**。
只有订阅之后新出现的条目才通知。这是避免刷屏的关键。
- **去重按「源 URL + GUID」**:不同源可能用相同 GUID只用 GUID 会互相误判。
GUID 缺失时回退用 `link`;两者都缺则跳过该条。
- `seenGUIDs` 有清理逻辑,不会无限增长。
- 解析用 [gofeed](https://github.com/mmcdole/gofeed)`v1.4.0`)。
## 构建
```bash
hmapdev build
```
## Install
Upload the .hmap file through the Plugin Manager API.

View File

@ -0,0 +1,48 @@
# sanitizer · 文本清洗
**不注册任何工具**,只挂三个阶段钩子,在 Agent 全链路上洗掉两类污染:
1. **坏字节**:坏 UTF-8、`U+FFFD`替换符、ANSI 转义序列
2. **思维泄漏**LLM 输出里残留的工具调用标记
## 为什么需要它
坏字节会**被 LLM 复读**。一次工具返回乱码(比如源码里带 ANSI 颜色码、或二进制片段被当文本读出来),
这些字节会进上下文,之后模型每次生成都可能把它抄一遍 —— 越滚越脏。
在每个入口洗掉,比事后清理便宜得多。
思维泄漏则是另一种:模型有时把 `<tool_call>...</tool_call>` 这类内部标记直接写进正文,
用户就看到一堆不该出现的 XML。
## 挂载的三个阶段
| 阶段 | 处理对象 | 作用 |
|---|---|---|
| `on_input` | `ctx.RawMessage` | 洗用户输入,脏字节不进后续链路 |
| `after_toolcall` | `ctx.ToolResults` | 洗工具结果,**坏字节不进 LLM 上下文** |
| `post_action` | `ctx.LLMText` | 洗模型输出:先清思维泄漏,再清乱码 |
每次有改动都打一行日志(`cleaned N bytes`),便于确认它真的在工作而不是静默失败。
## 识别哪些泄漏形态
按正则匹配多种标记写法,覆盖不同模型家族的习惯:
- `<tool_call>…</tool_call>``<invoke>…</invoke>``<tool>…</tool>`
- `<function>…</function>`
- 上述标记包在 ```xml / ```json 代码块里的形态
- 中文括号变体:`【tool_call】…【/tool_call】`
## 实现要点
- 依赖 **ABI v2 的 stage 写回能力**:插件对 `StageContext` 的修改会同步回内核。
在 v1 上改了不生效。
- 读写 `StageContext` 时按约定加 `ctx.Lock()`
## 构建
```bash
go build -buildmode=plugin -o sanitizer.so .
```
或经 `hmapdev build` 打包为 `.hmap`

77
example/vanblog/README.md Normal file
View File

@ -0,0 +1,77 @@
# vanblog · VanBlog 博客管理
用管理 API 操作 [VanBlog](https://vanblog.mereith.com/) 开源博客系统:
文章增删改查、分类标签、草稿发布、备份导出等。
## 配置项
| 键 | 默认 | 说明 |
|---|---|---|
| `url` | `https://blog.jianfgit.xyz` | VanBlog 站点基地址 |
| `token` | 空 | 管理员 API Token长期令牌从后台「Token 管理」创建) |
| `reset_token` | 空 | 用于 `auth/restore` 重置管理员密码的**特殊** Token |
`token``reset_token` 都是 `password` 类型(界面遮蔽)。
## 工具28 个)
### 文章
| 工具 | 说明 |
|---|---|
| `vanblog_list_articles` | 列文章,支持分页与搜索 |
| `vanblog_get_article` | 取单篇完整内容 |
| `vanblog_create_article` | 新建(`title``category` 必填) |
| `vanblog_update_article` | 更新(**只传要改的字段** |
| `vanblog_delete_article` | 删除(**软删除** |
| `vanblog_search_articles` | 按链接搜索文章 |
### 草稿
`vanblog_manage_drafts``list` / `get` / `create` / `update` / `delete` / **`publish`**
### 内容组织
| 工具 | 命令 |
|---|---|
| `vanblog_manage_categories` | `list` / `get` / `create` / `update` / `delete` |
| `vanblog_manage_tags` | `list` / `get` / `rename` / `delete` |
### 站点与运维
| 工具 | 说明 |
|---|---|
| `vanblog_manage_site` / `_settings` / `_menu` / `_social` / `_links` | 站点配置类 |
| `vanblog_manage_about` / `_pages` | 关于页与自定义页面 |
| `vanblog_manage_images` | 图床管理 |
| `vanblog_manage_rewards` | 赞赏配置 |
| `vanblog_manage_backup` | 备份 |
| `vanblog_manage_caddy` | Caddy 配置 |
| `vanblog_manage_isr` | ISR 增量静态渲染 |
| `vanblog_manage_pipelines` | 流水线 |
| `vanblog_manage_collaborators` | 协作者 |
| `vanblog_manage_tokens` | Token 管理 |
| `vanblog_get_analysis` / `_logs` / `_meta` | 统计、日志、元信息 |
| `vanblog_auth` | 认证相关(含 `restore` 重置密码) |
> 工具名前缀取自插件名(`tp`),上面按默认 `vanblog_` 列出。
## 实现要点
- 走的是 VanBlog 的管理 API`/api/admin/...`),所以必须配 **admin token**
不是前台只读接口。
- `update_article` 是**部分更新**:只传想改的字段,没传的保持不变。
(不要为了改标题而把正文一起传一遍。)
- `delete_article` 是**软删除**,内容仍在,可在后台恢复。
- 早期版本把 token 放在内核配置(`plugin.vanblog.token`)里,
现在会**自动迁移**到插件配置,迁移后清空内核侧取值。
## 前置
需要一个可访问的 VanBlog 实例,并在后台创建一个长期 Token。
## 构建
```bash
hmapdev build
```

View File

@ -1,13 +1,33 @@
# weather
# weather · 天气查询
weather plugin
给 agent 补上天气查询能力(基于 [wttr.in](https://wttr.in),无需 API Key
## Build
## 工具
| 工具 | 说明 |
|---|---|
| `weather_current` | 查询某城市当前天气 |
| `weather_forecast` | 查询未来几天预报 |
| `weather_set_location` | 设置默认城市 |
`weather_current` / `weather_forecast` 都接受 `location`(城市名,如 `Beijing``Shanghai`
省略时用配置里的默认城市。`weather_current` 另有 `units``metric`(摄氏,默认)或 `imperial`(华氏)。
## 配置项
| 键 | 默认 | 说明 |
|---|---|---|
| `default_location` | 空 | 默认城市名。留空则每次调用都必须传 `location` |
## 实现要点
- **`NoMemory: true`**:天气是外部实时数据,对记忆计算无长期价值,跳过向量化与关键词提取(原文仍保留在对话里)。
- **`Cleaner`**:输出参与记忆计算前先过滤,只保留摘要行 —— 天气查询会反复出现,全文进记忆会挤占上下文预算,而"上周三北京多少度"通常并不需要召回。
## 构建
```bash
hmapdev build
```
## Install
Upload the .hmap file through the Plugin Manager API.
产出 `.hmap` 后经 Plugin Manager API 安装。