@zxh126/pi-qqbot 1.4.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +129 -0
- package/README.md +17 -6
- package/docs/README.zh-CN.md +14 -5
- package/index.ts +122 -17
- package/package.json +2 -1
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
本文件记录 pi-qqbot 的版本变更。格式遵循 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/),
|
|
4
|
+
版本号遵循 [SemVer](https://semver.org/lang/zh-CN/)。
|
|
5
|
+
|
|
6
|
+
## [1.5.0] - 2026-10-25
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
|
|
10
|
+
- **暂停 / 恢复自动进度汇报(会话内有效)**:`/progress off`(别名 `/p pause`、`暂停`、`stop`、
|
|
11
|
+
「暂停汇报」「关闭自动汇报」)停掉定时推送,`/progress on`(别名 `resume`、`start`、`恢复`、
|
|
12
|
+
「恢复汇报」「继续汇报」)重新打开。
|
|
13
|
+
- 手动暂停跨 run 保留(`agent_start` 不再顺手复位),只在 `session_start`(`/new`、`/reload`、重启)复位为默认开启。
|
|
14
|
+
- 手动暂停期间,`/progress`、「进度」、「当前进度」、「现在进度」、「进展」**只回一次当前摘要**,
|
|
15
|
+
不会偷偷把 15 s 定时器打开,回复末尾附「(自动汇报已暂停,发 /progress on 恢复)」提示。
|
|
16
|
+
- 与原有的「配额静默」严格区分:配额静默是发送失败后的自动暂停,问一次进度即恢复;
|
|
17
|
+
手动暂停只有 `on`/「恢复汇报」能解除。
|
|
18
|
+
- `/progress <看不懂的参数>` 回用法提示,不再静默忽略。
|
|
19
|
+
- `/status` 新增 `自动汇报:` 一行,区分四种模式:`开启(每 Ns)` / `已暂停(本会话…)` /
|
|
20
|
+
`推送失败已静默` / `未启用(配置 progress.enabled=false)`。
|
|
21
|
+
- 裸文本口令「当前进度」「现在进度」「目前进展」(可带子命令,如「进度 暂停」)也走进度查询。
|
|
22
|
+
- 测试:`test/progress.test.mjs` 新增 7 个用例(暂停/恢复即时生效、跨 run 保留、暂停后手动查看不解除、
|
|
23
|
+
中文口令且不误伤任务句、空闲时问「当前进度」、`/status` 文案、未知子命令)。
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
|
|
27
|
+
- `/progress`(无参数)在**手动暂停**状态下不再解除暂停(此前任何一次问询都会清掉暂停标志)。
|
|
28
|
+
- 「推送失败已静默」与「用户手动暂停」拆成两个独立状态(`progressPaused` / `progressMuted`),
|
|
29
|
+
互不覆盖。
|
|
30
|
+
- README(英文)与 `docs/README.zh-CN.md` 的指令表、进度小节、`progress.enabled` 说明同步更新。
|
|
31
|
+
|
|
32
|
+
## [1.4.0] - 2026-10-24
|
|
33
|
+
|
|
34
|
+
### Added
|
|
35
|
+
|
|
36
|
+
- 任务运行期间按 `progress.intervalMs`(默认 15 s)推送进度摘要:轮次、工具调用次数与前三名、
|
|
37
|
+
失败次数、最近完成的工具(含耗时)、正在跑的工具、token 用量。全部由本地事件拼装,
|
|
38
|
+
不调用模型、不消耗 token。
|
|
39
|
+
- 全链路敏感信息脱敏(`redact.ts`):进度摘要、审批提示、队列预览、日志落盘统一过一遍,
|
|
40
|
+
覆盖 `Authorization` / QQ bot token / `sk-…` / `ghp_…` / `npm_…` / `AKIA…` / JWT / PEM /
|
|
41
|
+
`token=` / `password=` / `--api-key` / URL `user:pass@` / 高熵串。
|
|
42
|
+
- `/progress`(别名 `/p`、「进度」)与忙碌时的「到哪了」「怎么样了」问询。
|
|
43
|
+
- 最终答案因配额发不出去时缓存,下条消息以「(上一条回复发送失败,补发如下)」补发。
|
|
44
|
+
- 配置 `progress.enabled` / `progress.intervalMs`(环境变量 `PI_QQBOT_PROGRESS_MS` 可覆盖)。
|
|
45
|
+
|
|
46
|
+
## [1.3.3] - 2026-10-24
|
|
47
|
+
|
|
48
|
+
### Fixed
|
|
49
|
+
|
|
50
|
+
- `/usage` 报告读回改为轮询等待:`appendEntry` 晚于命令 `await` 返回,导致偶发读不到刚写入的报告。
|
|
51
|
+
|
|
52
|
+
## [1.3.2] - 2026-10-24
|
|
53
|
+
|
|
54
|
+
### Fixed
|
|
55
|
+
|
|
56
|
+
- `/usage` 读回兜底:`getEntries` 尚未刷新时直接读会话文件尾部;`generatedAt` 兼容毫秒时间戳。
|
|
57
|
+
|
|
58
|
+
## [1.3.1] - 2026-10-24
|
|
59
|
+
|
|
60
|
+
### Fixed
|
|
61
|
+
|
|
62
|
+
- `/usage rate` 真正执行并读回配置文件确认,不再「口头成功」。
|
|
63
|
+
|
|
64
|
+
## [1.3.0] - 2026-10-24
|
|
65
|
+
|
|
66
|
+
### Added
|
|
67
|
+
|
|
68
|
+
- `/usage`、`/cost`(只往 `ctx.ui.*` 输出的扩展命令)桥接回 QQ,不再沉默。
|
|
69
|
+
- `/mcp`、`/llama` 这类需要主机 TUI 选择框/登录的命令改为明确回执说明。
|
|
70
|
+
|
|
71
|
+
## [1.2.1] - 2026-10-24
|
|
72
|
+
|
|
73
|
+
### Changed
|
|
74
|
+
|
|
75
|
+
- 日志时间戳改为本地时间并带 UTC 偏移(如 `[2026-10-24T07:39:09.762+08:00]`),与文件 mtime、
|
|
76
|
+
进程启动时间直接对齐,不再需要心算时区。
|
|
77
|
+
|
|
78
|
+
## [1.2.0] - 2026-10-24
|
|
79
|
+
|
|
80
|
+
### Added
|
|
81
|
+
|
|
82
|
+
- 忙碌时收到的消息进入队列,当前任务 settle 后自动续跑;`/cancel` 永远不入队、立即生效。
|
|
83
|
+
- `/queue`(别名 `/q`)查看队列,`/queue clear`、`/queue drop <编号>` 管理队列。
|
|
84
|
+
- 配置 `queue.max`(0 = 关闭排队)、`queue.ack`。
|
|
85
|
+
|
|
86
|
+
## [1.1.0] - 2026-10-24
|
|
87
|
+
|
|
88
|
+
### Added
|
|
89
|
+
|
|
90
|
+
- `/tree`、`/fork`、`/resume` 的编号选择器接管:回一个最多 10 项的列表,只对发出者有效、10 分钟过期;
|
|
91
|
+
- `/clone` 支持。
|
|
92
|
+
|
|
93
|
+
## [1.0.0] - 2026-10-24
|
|
94
|
+
|
|
95
|
+
### Added
|
|
96
|
+
|
|
97
|
+
- 接管 pi 的斜杠命令(白名单 `commands.passthrough` / `allowReload` / `allowQuit` / `allowSession`):
|
|
98
|
+
`/reload`、`/new`、`/compact`、`/session`、`/name`、`/model`、`/thinking`、`/quit`,
|
|
99
|
+
以及扩展命令、prompt 模板、`/skill:name` 的透传。
|
|
100
|
+
- 有任务在跑时,`/reload`、`/new`、`/compact`、`/quit`、`/tree`、`/fork`、`/clone`、`/resume` 拒绝执行。
|
|
101
|
+
|
|
102
|
+
## [0.3.0] - 2026-10-24
|
|
103
|
+
|
|
104
|
+
### Added
|
|
105
|
+
|
|
106
|
+
- `/cancel`(别名 `/stop`、`/abort`、「取消」「停止」「中止」「终止」「算了」)中断正在运行的任务:
|
|
107
|
+
先终止待审批、清空队列,再 `ctx.abort()`,丢弃半截回复;8 秒看门狗兜底释放忙碌标记。
|
|
108
|
+
|
|
109
|
+
## [0.2.1] - 2026-10-24
|
|
110
|
+
|
|
111
|
+
### Changed
|
|
112
|
+
|
|
113
|
+
- 中文 README 移入 `docs/README.zh-CN.md`,修复 npm 页面默认 readme 被中文版顶掉。
|
|
114
|
+
|
|
115
|
+
## [0.2.0] - 2026-10-24
|
|
116
|
+
|
|
117
|
+
### Added
|
|
118
|
+
|
|
119
|
+
- 会话级「以后都同意」自动放行(`approve.remember`),含已在等待的那一条;
|
|
120
|
+
「取消自动同意」「恢复审批」等**长得像取消**的说法仍走审批分支。
|
|
121
|
+
- 中英文双语 README。
|
|
122
|
+
|
|
123
|
+
## [0.1.0] - 2026-10-16
|
|
124
|
+
|
|
125
|
+
### Added
|
|
126
|
+
|
|
127
|
+
- 初始版本:把 Pi coding agent 接入 QQ 单聊(C2C),单会话 + `proper-lockfile` 单实例锁(宿主接管)、
|
|
128
|
+
危险工具(`bash` / `powershell` / `write` / `edit` 及 `destructiveHint` 工具)执行前远程审批、
|
|
129
|
+
长回复自动分段。
|
package/README.md
CHANGED
|
@@ -17,9 +17,9 @@ Bridge the **[Pi](https://pi.dev) coding agent** into QQ private (C2C) chats: se
|
|
|
17
17
|
| **Single-instance lock** | Only one pi process hosts the QQ gateway; the others stand by and take over automatically when the host exits. |
|
|
18
18
|
| **Cancel a running task** | Send `/cancel` (or 「取消」/「停止」) and the current run is aborted immediately — the QQ reply, the pending tool calls, the half-written answer and the pending queue are all cleaned up. |
|
|
19
19
|
| **Slash-command routing** | QQ messages starting with `/` are routed: pi extension commands, prompt templates and `/skill:name` are dispatched to pi; a set of pi built-ins (`/reload`, `/new`, `/compact`, `/session`, `/name`, `/model`, `/thinking`, `/quit`) is taken over; host-only commands are answered instead of being fed to the model. |
|
|
20
|
-
| **Progress reports** | While a run is in progress the bot posts a short progress summary (turns, tool calls, the tool currently running, tokens) every `progress.intervalMs` (default **15 s**). Ask at any time with `/progress` (aliases `/p`, 「进度」). If a report cannot be delivered the automatic reports stay quiet until you ask again. |
|
|
20
|
+
| **Progress reports** | While a run is in progress the bot posts a short progress summary (turns, tool calls, the tool currently running, tokens) every `progress.intervalMs` (default **15 s**). Ask at any time with `/progress` (aliases `/p`, 「进度」). `/progress off` pauses the automatic reports for the rest of the session, `/progress on` brings them back. If a report cannot be delivered the automatic reports stay quiet until you ask again. |
|
|
21
21
|
| **Secret redaction** | Anything leaving the machine is scrubbed first — progress reports, approval prompts, queue previews and the log file: `Authorization` headers (incl. QQ bot tokens), `sk-…` / `ghp_…` / `npm_…` / `AKIA…` / JWTs / PEM blocks, `token=` / `password=` / `--api-key` values, URL `user:pass@`, and long high-entropy strings. |
|
|
22
|
-
| **Built-in commands** | `/status`, `/queue`, `/progress
|
|
22
|
+
| **Built-in commands** | `/status`, `/queue`, `/progress` (+ `/progress off` / `on`), `/reset`, `/cancel`, `/help`. |
|
|
23
23
|
|
|
24
24
|
## Install
|
|
25
25
|
|
|
@@ -63,7 +63,7 @@ Then create `~/.pi/agent/pi-qqbot.json`:
|
|
|
63
63
|
| `approve.tools` | `["bash","powershell","write","edit"]` | Tools that require approval; tools annotated `destructiveHint` are always gated too |
|
|
64
64
|
| `approve.remember` | `true` | Remember the decision per tool inside the session (no more prompts for that tool) |
|
|
65
65
|
| `commands` | see below | Slash-command routing gates: `passthrough`, `allowReload`, `allowQuit`, `allowSession` |
|
|
66
|
-
| `progress.enabled` | `true` | Post periodic progress reports while a task is running |
|
|
66
|
+
| `progress.enabled` | `true` | Post periodic progress reports while a task is running (read at session start — restart or `/new` after changing; at runtime use `/progress off`) |
|
|
67
67
|
| `progress.intervalMs` | `15000` | Interval of those reports (env override: `PI_QQBOT_PROGRESS_MS`) |
|
|
68
68
|
| `queue.max` | `20` | How many messages may wait while a task runs; `0` disables queueing (busy messages are refused as before) |
|
|
69
69
|
| `queue.ack` | `true` | Reply to QQ when a queued [message](#queueing-while-busy) starts (the "queued" ack is always sent) |
|
|
@@ -77,9 +77,11 @@ Start pi, then message the bot in QQ; pi handles it and replies.
|
|
|
77
77
|
| Command | Description |
|
|
78
78
|
|---|---|
|
|
79
79
|
| `/reset` | Clear the reply buffer, approval memory and session-wide auto-approve (**does not** switch sessions — send `/new` for that) |
|
|
80
|
-
| `/status` | Show busy/idle state, queue length, auto-approve state
|
|
80
|
+
| `/status` | Show busy/idle state, queue length, auto-approve state, pending approval count and the progress-report mode |
|
|
81
81
|
| `/queue` | Show the queue (aliases: `/q`); `/queue clear` empties it, `/queue drop <id>` removes one entry |
|
|
82
|
-
| `/progress` | **Show the current progress summary immediately**
|
|
82
|
+
| `/progress` | **Show the current progress summary immediately** (aliases: `/p`, 「进度」, 「当前进度」, 「进展」; plain-text questions like 「到哪了」「怎么样了」 work while busy). While you paused reports yourself it only replies once — use `/progress on` to resume; while reports are merely quota-quiet, asking resumes them |
|
|
83
|
+
| `/progress off` | **Pause automatic progress reports for the rest of the session** (aliases: `pause`, `stop`, `暂停`, 「暂停汇报」, 「关闭汇报」). Manual `/progress` still works, it just no longer resumes the timer |
|
|
84
|
+
| `/progress on` | **Resume automatic progress reports** (aliases: `resume`, `start`, `恢复`, 「恢复汇报」, 「继续汇报」) |
|
|
83
85
|
| `/cancel` | **Interrupt the task that is running right now** and empty the queue (aliases: `/stop`, `/abort`, 「取消」, 「停止」, 「中止」, 「终止」, 「算了」) |
|
|
84
86
|
| `/help` | Show help |
|
|
85
87
|
|
|
@@ -197,10 +199,14 @@ While a run is in progress, a summary is pushed to QQ every `progress.intervalMs
|
|
|
197
199
|
- **Quota-aware**: one QQ message buys only 4 passive replies, so a long run will eventually run out.
|
|
198
200
|
The first failed send stops automatic reports for that run (no retry spam); send `/progress`
|
|
199
201
|
(or 「进度」) and they resume.
|
|
202
|
+
- **Manual pause / resume**: `/progress off` (or 「暂停汇报」) silences the reports for the rest of the
|
|
203
|
+
session — it survives `agent_settled` and the next run, and asking for `/progress` no longer turns it
|
|
204
|
+
back on. `/progress on` (or 「恢复汇报」) resumes. A new session (`/new`, restart) starts enabled again.
|
|
205
|
+
`/status` shows which of the four modes you are in (on / paused-by-you / quota-quiet / disabled).
|
|
200
206
|
- **The final answer is protected**: if it cannot be delivered (quota), it is cached and re-sent with a
|
|
201
207
|
`(上一条回复发送失败,补发如下)` prefix as soon as you send the next message.
|
|
202
208
|
- Timer is cleared on `agent_settled` / `/cancel` / session shutdown.
|
|
203
|
-
- Set `progress.enabled: false` to turn the whole thing off.
|
|
209
|
+
- Set `progress.enabled: false` to turn the whole thing off (across sessions; read at session start).
|
|
204
210
|
|
|
205
211
|
### Cancelling a run
|
|
206
212
|
|
|
@@ -266,6 +272,11 @@ The QQ gateway keeps only one active connection per bot. The extension uses a `p
|
|
|
266
272
|
|
|
267
273
|
Debug logs are written to `~/.pi/agent/pi-qqbot.log`. Each line starts with a **local** timestamp that carries the UTC offset, e.g. `[2026-10-24T07:39:09.762+08:00]`, so it lines up with file mtimes and process start times without mental arithmetic.
|
|
268
274
|
|
|
275
|
+
## Changelog
|
|
276
|
+
|
|
277
|
+
See [CHANGELOG.md](./CHANGELOG.md) — every release with what changed and why. The progress-report
|
|
278
|
+
pause/resume (`/progress off` / `on`) landed in **1.5.0**.
|
|
279
|
+
|
|
269
280
|
## License
|
|
270
281
|
|
|
271
282
|
MIT
|
package/docs/README.zh-CN.md
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
| **单实例锁** | 只有一个 pi 进程作为宿主连接 QQ,其余待命,宿主退出后自动接管。 |
|
|
18
18
|
| **中断正在运行的任务** | 发 `/cancel`(或「取消」/「停止」),当前任务立刻中断 —— QQ 回复、待审批工具调用、写到一半的回答、排队中的消息一并清理。 |
|
|
19
19
|
| **斜杠命令路由** | 以 `/` 开头的 QQ 消息会先被路由:pi 的扩展命令、prompt 模板、`/skill:name` 交给 pi 派发;一部分 pi 内置命令(`/reload`、`/new`、`/compact`、`/session`、`/name`、`/model`、`/thinking`、`/quit`)由本扩展接管;只能在主机执行的内置命令直接回执,不会送给模型。 |
|
|
20
|
-
| **进度汇报** | 任务运行期间按 `progress.intervalMs`(默认 **15 秒**)推一条进度摘要(轮次、工具调用、正在跑的工具、token 用量)。随时发 `/progress`(别名 `/p
|
|
20
|
+
| **进度汇报** | 任务运行期间按 `progress.intervalMs`(默认 **15 秒**)推一条进度摘要(轮次、工具调用、正在跑的工具、token 用量)。随时发 `/progress`(别名 `/p`、「进度」)可立即获取;`/progress off` 在本会话内暂停自动汇报,`/progress on` 恢复。推送失败后自动推送静默,直到你再次问询。 |
|
|
21
21
|
| **敏感信息脱敏** | 离开本机的文本先脱敏——进度摘要、审批提示、队列预览、日志文件:`Authorization` 头(含 QQ bot token)、`sk-…`/`ghp_…`/`npm_…`/`AKIA…`/JWT/PEM、`token=`/`password=`/`--api-key` 的值、URL 里的 `user:pass@`,以及超长高熵串。 |
|
|
22
22
|
| **内置指令** | `/status`、`/queue`、`/progress`、`/reset`、`/cancel`、`/help`。 |
|
|
23
23
|
|
|
@@ -65,7 +65,7 @@ pi install ./pi-qqbot
|
|
|
65
65
|
| `commands` | 见下 | 斜杠命令路由开关:`passthrough`、`allowReload`、`allowQuit`、`allowSession` |
|
|
66
66
|
| `queue.max` | `20` | 任务运行期间最多排队多少条;`0` = 关闭排队(忙碌时直接拒绍,回到旧行为) |
|
|
67
67
|
| `queue.ack` | `true` | 排队消息开始执行时是否回执 QQ(入队回执总是会发) |
|
|
68
|
-
| `progress.enabled` | `true` |
|
|
68
|
+
| `progress.enabled` | `true` | 任务运行期间是否定期推送进度摘要(会话启动时读取,改完需 `/new` 或重启;运行时用 `/progress off`) |
|
|
69
69
|
| `progress.intervalMs` | `15000` | 推送间隔(可用环境变量 `PI_QQBOT_PROGRESS_MS` 覆盖,便于测试) |
|
|
70
70
|
|
|
71
71
|
也可用环境变量 `QQ_BOT_APPID` / `QQ_BOT_APPSECRET` 代替文件里的凭据。
|
|
@@ -77,9 +77,11 @@ pi install ./pi-qqbot
|
|
|
77
77
|
| 指令 | 说明 |
|
|
78
78
|
|---|---|
|
|
79
79
|
| `/reset` | 重置:清空回复缓冲、审批记忆与自动同意开关(**不换会话**,换会话发 `/new`) |
|
|
80
|
-
| `/status` |
|
|
80
|
+
| `/status` | 查看忙闲状态、队列长度、自动同意状态、待审批数量、自动汇报模式 |
|
|
81
81
|
| `/queue` | 查看队列(别名 `/q`);`/queue clear` 清空,`/queue drop <编号>` 删一条 |
|
|
82
|
-
| `/progress` |
|
|
82
|
+
| `/progress` | **立即获取当前进度摘要**(别名 `/p`、「进度」「当前进度」「进展」;忙碌时「到哪了」「怎么样了」这类问法同样生效)。手动暂停期间**问一次只回一次**,要重新打开定时汇报需发 `/progress on` 或「恢复汇报」;若只是配额静默,问一次即恢复 |
|
|
83
|
+
| `/progress off` | **在本会话内暂停自动进度汇报**(别名:`pause`、`stop`、`暂停`、「暂停汇报」、「关闭汇报」)。手动 `/progress` 仍可看一次,但不再解除暂停 |
|
|
84
|
+
| `/progress on` | **恢复自动进度汇报**(别名:`resume`、`start`、`恢复`、「恢复汇报」、「继续汇报」) |
|
|
83
85
|
| `/cancel` | **中断当前正在运行的任务**并清空队列(别名:`/stop`、`/abort`、「取消」、「停止」、「中止」、「终止」、「算了」) |
|
|
84
86
|
| `/help` | 帮助 |
|
|
85
87
|
|
|
@@ -187,10 +189,13 @@ pi 的会话一次只跑一轮 agent run,所以串行调度由 bot 自己做
|
|
|
187
189
|
- 工具参数先脱敏再截断,带凭据的命令不会明文出现在 QQ 里。
|
|
188
190
|
- **配额感知**:一条用户消息只有 4 次被动回复额度,长任务迟早会用完。第一次发送失败就停掉本轮
|
|
189
191
|
的自动推送(不重试、不刷屏);你发 `/progress`(或「进度」)即恢复。
|
|
192
|
+
- **手动暂停/恢复**:`/progress off`(或「暂停汇报」)在本会话内停掉自动推送——跨 run 保留,
|
|
193
|
+
之后问 `/progress` 也不会把它打开;`/progress on`(或「恢复汇报」)恢复。开新会话(`/new`、重启)后
|
|
194
|
+
恢复默认开启。`/status` 会显示当前处于哪种模式(开启 / 手动暂停 / 配额静默 / 配置关闭)。
|
|
190
195
|
- **保护最终答案**:答案因配额发不出去时会被缓存,等你发下一条消息时以
|
|
191
196
|
`(上一条回复发送失败,补发如下)` 前缀补发。
|
|
192
197
|
- `agent_settled` / `/cancel` / 会话关闭时定时器都会清掉。
|
|
193
|
-
-
|
|
198
|
+
- 想彻底关掉(跨会话):`progress.enabled: false`(改完需 `/new` 或重启才生效)。
|
|
194
199
|
|
|
195
200
|
### 取消正在运行的任务
|
|
196
201
|
|
|
@@ -253,6 +258,10 @@ QQ 网关对同一 bot 只保留一个活跃连接。插件用 `proper-lockfile`
|
|
|
253
258
|
|
|
254
259
|
调试日志写入 `~/.pi/agent/pi-qqbot.log`。每行开头是**本地时间 + 时区偏移**(如 `[2026-10-24T07:39:09.762+08:00]`),和文件 mtime、进程启动时间直接对得上,不需要再拿 UTC 换算。
|
|
255
260
|
|
|
261
|
+
## 变更日志
|
|
262
|
+
|
|
263
|
+
见 [CHANGELOG.md](../CHANGELOG.md):每个版本改了什么、为什么改。进度汇报的暂停/恢复(`/progress off` / `on`)在 **1.5.0** 加入。
|
|
264
|
+
|
|
256
265
|
## License
|
|
257
266
|
|
|
258
267
|
MIT
|
package/index.ts
CHANGED
|
@@ -60,6 +60,8 @@ interface State {
|
|
|
60
60
|
progressTimer: ReturnType<typeof setInterval> | null;
|
|
61
61
|
/** 推送失败后暂停自动推送,直到用户主动询问 */
|
|
62
62
|
progressPaused: boolean;
|
|
63
|
+
/** 用户手动暂停自动推送(本会话有效:跨 run 保留,/progress on 恢复) */
|
|
64
|
+
progressMuted: boolean;
|
|
63
65
|
/** 最终答案发送失败时缓存,下一条消息时补发 */
|
|
64
66
|
undelivered: { openid: string; text: string } | null;
|
|
65
67
|
}
|
|
@@ -90,6 +92,7 @@ export function createInitialState(): State {
|
|
|
90
92
|
progress: new ProgressTracker(),
|
|
91
93
|
progressTimer: null,
|
|
92
94
|
progressPaused: false,
|
|
95
|
+
progressMuted: false,
|
|
93
96
|
undelivered: null,
|
|
94
97
|
};
|
|
95
98
|
}
|
|
@@ -242,6 +245,9 @@ export default function (
|
|
|
242
245
|
state.cfg = cfg;
|
|
243
246
|
// 新会话:清空 remember 记忆与自动同意开关(自动同意只在当前会话有效)
|
|
244
247
|
state.approver.reset();
|
|
248
|
+
// 手动暂停的自动汇报同样只在本会话有效
|
|
249
|
+
state.progressMuted = false;
|
|
250
|
+
state.progressPaused = false;
|
|
245
251
|
// 队列上限来自配置;新会话不继承上一个会话遗留的待执行任务
|
|
246
252
|
state.queue.setLimit(cfg.queue.max);
|
|
247
253
|
state.queue.clear();
|
|
@@ -719,7 +725,8 @@ export async function handleMessage(
|
|
|
719
725
|
msg.openid,
|
|
720
726
|
[
|
|
721
727
|
"pi-qqbot 指令:",
|
|
722
|
-
"/
|
|
728
|
+
"/progress — 查看进度;/progress off 暂停自动汇报、/progress on 恢复(本会话有效)",
|
|
729
|
+
"/status — 查看状态(忙闲 / 队列 / 自动同意 / 待审批 / 自动汇报)",
|
|
723
730
|
"/queue — 查看队列;/queue clear 清空;/queue drop <编号> 删一条",
|
|
724
731
|
"/reset — 清空回复缓冲、审批记忆与自动同意(不换会话;换会话发 /new)",
|
|
725
732
|
"/cancel(或「取消」「停止」)— 中断正在运行的任务并清空队列",
|
|
@@ -737,24 +744,66 @@ export async function handleMessage(
|
|
|
737
744
|
`队列: ${state.queue.size} 条待执行${state.queue.size > 0 ? "(发 /queue 查看)" : ""}`,
|
|
738
745
|
`自动同意: ${state.approver.autoAllowed ? "已开启(本会话有效)" : "未开启"}`,
|
|
739
746
|
`待审批: ${state.approver.pendingCount}`,
|
|
747
|
+
`自动汇报: ${describeProgressMode(state)}`,
|
|
740
748
|
];
|
|
741
749
|
if (state.busy) {
|
|
742
750
|
const snapshot = state.progress.summary();
|
|
743
751
|
if (snapshot) lines.push("", snapshot);
|
|
744
|
-
//
|
|
745
|
-
state.progressPaused = false;
|
|
752
|
+
// 用户主动问询:恢复自动推送(手动暂停的不算)
|
|
753
|
+
if (!state.progressMuted) state.progressPaused = false;
|
|
746
754
|
}
|
|
747
755
|
await replyTo(api, state, msg.openid, lines.join("\n"));
|
|
748
756
|
return;
|
|
749
757
|
}
|
|
750
758
|
if (cmd?.name === "progress") {
|
|
759
|
+
const sub = (cmd.args ?? "").trim().toLowerCase();
|
|
760
|
+
if (MUTE_ARGS.has(sub)) {
|
|
761
|
+
const was = state.progressMuted;
|
|
762
|
+
state.progressMuted = true;
|
|
763
|
+
state.progressPaused = false;
|
|
764
|
+
log("[progress] 用户暂停自动进度推送(本会话有效)", was ? "(已经是暂停状态)" : "");
|
|
765
|
+
await replyTo(
|
|
766
|
+
api,
|
|
767
|
+
state,
|
|
768
|
+
msg.openid,
|
|
769
|
+
was
|
|
770
|
+
? "自动汇报本来就已暂停。发 /progress on(或「恢复汇报」)可以恢复。"
|
|
771
|
+
: "已暂停自动进度汇报(本会话有效,换会话或重启后恢复默认开启)。需要时发 /progress 仍可查看一次,发 /progress on 恢复。",
|
|
772
|
+
);
|
|
773
|
+
return;
|
|
774
|
+
}
|
|
775
|
+
if (UNMUTE_ARGS.has(sub)) {
|
|
776
|
+
const was = state.progressMuted;
|
|
777
|
+
state.progressMuted = false;
|
|
778
|
+
state.progressPaused = false;
|
|
779
|
+
log("[progress] 用户恢复自动进度推送");
|
|
780
|
+
await replyTo(
|
|
781
|
+
api,
|
|
782
|
+
state,
|
|
783
|
+
msg.openid,
|
|
784
|
+
was
|
|
785
|
+
? `已恢复自动进度汇报${state.cfg?.progress?.enabled === false ? "(但配置 progress.enabled=false,仍不会推送)" : ""}。`
|
|
786
|
+
: "自动汇报本来就是开启的。",
|
|
787
|
+
);
|
|
788
|
+
return;
|
|
789
|
+
}
|
|
790
|
+
if (sub) {
|
|
791
|
+
await replyTo(
|
|
792
|
+
api,
|
|
793
|
+
state,
|
|
794
|
+
msg.openid,
|
|
795
|
+
"用法: /progress(看一次)| /progress off(暂停自动汇报)| /progress on(恢复)。也可以说「暂停汇报」「恢复汇报」。",
|
|
796
|
+
);
|
|
797
|
+
return;
|
|
798
|
+
}
|
|
751
799
|
const snapshot = state.progress.summary();
|
|
752
|
-
state.progressPaused = false;
|
|
800
|
+
if (!state.progressMuted) state.progressPaused = false;
|
|
801
|
+
const body = snapshot || (state.busy ? "当前任务刚启动,还没有可汇报的进度。" : "当前没有正在运行的任务。");
|
|
753
802
|
await replyTo(
|
|
754
803
|
api,
|
|
755
804
|
state,
|
|
756
805
|
msg.openid,
|
|
757
|
-
|
|
806
|
+
state.progressMuted ? `${body}\n(自动汇报已暂停,发 /progress on 恢复)` : body,
|
|
758
807
|
);
|
|
759
808
|
return;
|
|
760
809
|
}
|
|
@@ -774,7 +823,7 @@ export async function handleMessage(
|
|
|
774
823
|
// 忙碌时问「进度」不该排队,直接回当前进度并恢复自动推送
|
|
775
824
|
if (RE_PROGRESS_QUERY.test(msg.content.trim())) {
|
|
776
825
|
const snapshot = state.progress.summary();
|
|
777
|
-
state.progressPaused = false;
|
|
826
|
+
if (!state.progressMuted) state.progressPaused = false;
|
|
778
827
|
await replyTo(api, state, msg.openid, snapshot || "任务刚启动,还没有可汇报的进度。");
|
|
779
828
|
return;
|
|
780
829
|
}
|
|
@@ -845,9 +894,9 @@ function stopProgressTimer(state: State) {
|
|
|
845
894
|
}
|
|
846
895
|
}
|
|
847
896
|
|
|
848
|
-
/**
|
|
897
|
+
/** 推一条进度摘要;失败就暂停自动推送(用户问一次即恢复);手动暂停(/progress off)管到底 */
|
|
849
898
|
async function pushProgress(state: State, reason: "tick" | "ask"): Promise<boolean> {
|
|
850
|
-
if (reason === "tick" && state.progressPaused) return false;
|
|
899
|
+
if (reason === "tick" && (state.progressPaused || state.progressMuted)) return false;
|
|
851
900
|
const api = state.api;
|
|
852
901
|
const openid = state.currentOpenid ?? state.lastOpenid;
|
|
853
902
|
if (!api || !openid || !state.busy || !state.progress.running) return false;
|
|
@@ -861,19 +910,75 @@ async function pushProgress(state: State, reason: "tick" | "ask"): Promise<boole
|
|
|
861
910
|
return ok;
|
|
862
911
|
}
|
|
863
912
|
|
|
913
|
+
/** 自动汇报当前状态的人话描述(/status 用) */
|
|
914
|
+
function describeProgressMode(state: State): string {
|
|
915
|
+
const cfg = state.cfg?.progress;
|
|
916
|
+
if (state.progressMuted) return "已暂停(本会话,发 /progress on 恢复)";
|
|
917
|
+
if (cfg?.enabled === false) return "未启用(配置 progress.enabled=false)";
|
|
918
|
+
if (state.progressPaused) return "推送失败已静默,发 /progress 恢复";
|
|
919
|
+
const ms = Number.isFinite(cfg?.intervalMs) && (cfg?.intervalMs ?? 0) >= 50 ? cfg!.intervalMs : 15000;
|
|
920
|
+
return `开启(每 ${Math.round(ms / 1000)}s)`;
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
/** /progress 的子命令:暂停 / 恢复自动汇报 */
|
|
924
|
+
const MUTE_ARGS = new Set([
|
|
925
|
+
"off",
|
|
926
|
+
"pause",
|
|
927
|
+
"stop",
|
|
928
|
+
"mute",
|
|
929
|
+
"关",
|
|
930
|
+
"关闭",
|
|
931
|
+
"关掉",
|
|
932
|
+
"停",
|
|
933
|
+
"停止",
|
|
934
|
+
"暂停",
|
|
935
|
+
"暂停汇报",
|
|
936
|
+
"停止汇报",
|
|
937
|
+
]);
|
|
938
|
+
const UNMUTE_ARGS = new Set([
|
|
939
|
+
"on",
|
|
940
|
+
"resume",
|
|
941
|
+
"start",
|
|
942
|
+
"unmute",
|
|
943
|
+
"开",
|
|
944
|
+
"开启",
|
|
945
|
+
"打开",
|
|
946
|
+
"恢复",
|
|
947
|
+
"继续",
|
|
948
|
+
"恢复汇报",
|
|
949
|
+
"开启汇报",
|
|
950
|
+
"继续汇报",
|
|
951
|
+
]);
|
|
952
|
+
|
|
953
|
+
/** 不带斜杠的「暂停汇报」类说法(整句匹配,避免误伤正常聊天) */
|
|
954
|
+
const RE_REPORTS_OFF =
|
|
955
|
+
/^(?:请|帮我|给我)?\s*(?:暂停|停止|关闭|关掉|别|不要|别再)(?:自动)?(?:的)?(?:进度汇报|进度|进展|汇报|推送|播报|报告)(?:上报|推送|汇报)?(?:了|吧)?$/;
|
|
956
|
+
const RE_REPORTS_ON =
|
|
957
|
+
/^(?:请|帮我|给我)?\s*(?:恢复|继续|开启|打开|开始)(?:自动)?(?:的)?(?:进度汇报|进度|进展|汇报|推送|播报|报告)(?:上报|推送|汇报)?(?:吧)?$/;
|
|
958
|
+
|
|
959
|
+
/** 不带斜杠的进度问询:「进度」「当前进度」「现在进展」(可带子命令) */
|
|
960
|
+
const BARE_PROGRESS_RE = /^(?:progress|(?:当前|现在|目前)?的?(?:进度|进展))(?:\s+([\s\S]*))?$/i;
|
|
961
|
+
|
|
864
962
|
function parseCommand(text: string): { name: "reset" | "status" | "help" | "queue" | "progress"; args: string } | null {
|
|
865
963
|
// 注意:/new 现在属于 pi 的内置命令(由 slash 路由接管,真的开新会话),
|
|
866
964
|
// 本地的「清缓冲/清审批记忆」改名为 /reset。
|
|
867
965
|
// 斜杠形式:全部内置指令;不带斜杠时只接受明确表达「进度」的词,避免把聊天里的 "p" / "status" 误判成命令
|
|
868
|
-
const
|
|
869
|
-
|
|
870
|
-
if (
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
966
|
+
const t = text.trim();
|
|
967
|
+
const slash = /^\/(reset|status|help|queue|q|progress|p)(?:\s+([\s\S]*))?$/i.exec(t);
|
|
968
|
+
if (slash) {
|
|
969
|
+
const raw = slash[1].toLowerCase();
|
|
970
|
+
const name = raw === "q" ? "queue" : raw === "p" ? "progress" : raw;
|
|
971
|
+
return {
|
|
972
|
+
name: name as "reset" | "status" | "help" | "queue" | "progress",
|
|
973
|
+
args: (slash[2] ?? "").trim(),
|
|
974
|
+
};
|
|
975
|
+
}
|
|
976
|
+
// 裸文本:「进度」「当前进度」→ 看一次;「暂停汇报」「恢复汇报」→ 开关
|
|
977
|
+
const bare = BARE_PROGRESS_RE.exec(t);
|
|
978
|
+
if (bare) return { name: "progress", args: (bare[1] ?? "").trim() };
|
|
979
|
+
if (RE_REPORTS_OFF.test(t)) return { name: "progress", args: "off" };
|
|
980
|
+
if (RE_REPORTS_ON.test(t)) return { name: "progress", args: "on" };
|
|
981
|
+
return null;
|
|
877
982
|
}
|
|
878
983
|
|
|
879
984
|
/** /queue [clear|drop <编号>] */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zxh126/pi-qqbot",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Bridge the Pi coding agent into QQ private chats, with remote tool-call approval",
|
|
6
6
|
"license": "MIT",
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
"qq",
|
|
35
35
|
"util",
|
|
36
36
|
"README.md",
|
|
37
|
+
"CHANGELOG.md",
|
|
37
38
|
"docs",
|
|
38
39
|
"LICENSE"
|
|
39
40
|
],
|