@zhin.js/plugin-reminder 0.0.0-stage → 1.0.1
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/README.md +87 -2
- package/commands/remind/after/index.js +16 -0
- package/commands/remind/after/index.ts +16 -0
- package/commands/remind/at/index.js +22 -0
- package/commands/remind/at/index.ts +16 -0
- package/commands/remind/cancel/index.js +11 -0
- package/commands/remind/cancel/index.ts +11 -0
- package/commands/remind/every/index.js +16 -0
- package/commands/remind/every/index.ts +16 -0
- package/commands/remind/list/index.js +14 -0
- package/commands/remind/list/index.ts +14 -0
- package/lib/caller.d.ts +5 -0
- package/lib/caller.d.ts.map +1 -0
- package/lib/caller.js +18 -0
- package/lib/caller.js.map +1 -0
- package/lib/config.d.ts +17 -0
- package/lib/config.d.ts.map +1 -0
- package/lib/config.js +17 -0
- package/lib/config.js.map +1 -0
- package/lib/duration.d.ts +2 -0
- package/lib/duration.d.ts.map +1 -0
- package/lib/duration.js +31 -0
- package/lib/duration.js.map +1 -0
- package/lib/index.d.ts +5 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +5 -0
- package/lib/index.js.map +1 -0
- package/lib/runtime.d.ts +3 -0
- package/lib/runtime.d.ts.map +1 -0
- package/lib/runtime.js +3 -0
- package/lib/runtime.js.map +1 -0
- package/lib/service.d.ts +42 -0
- package/lib/service.d.ts.map +1 -0
- package/lib/service.js +180 -0
- package/lib/service.js.map +1 -0
- package/lib/tool-actor.d.ts +20 -0
- package/lib/tool-actor.d.ts.map +1 -0
- package/lib/tool-actor.js +38 -0
- package/lib/tool-actor.js.map +1 -0
- package/package.json +94 -3
- package/plugin.js +41 -0
- package/schedules/poll/index.js +8 -0
- package/schedules/poll/index.ts +8 -0
- package/skills/reminder/SKILL.md +45 -0
- package/skills/reminder/tools/reminder_cancel/index.js +24 -0
- package/skills/reminder/tools/reminder_cancel/index.ts +25 -0
- package/skills/reminder/tools/reminder_create/index.js +57 -0
- package/skills/reminder/tools/reminder_create/index.ts +77 -0
- package/skills/reminder/tools/reminder_list/index.js +22 -0
- package/skills/reminder/tools/reminder_list/index.ts +22 -0
- package/src/caller.ts +13 -0
- package/src/config.ts +16 -0
- package/src/duration.ts +31 -0
- package/src/index.ts +4 -0
- package/src/runtime.ts +4 -0
- package/src/service.ts +171 -0
- package/src/tool-actor.ts +52 -0
package/README.md
CHANGED
|
@@ -1,3 +1,88 @@
|
|
|
1
|
-
#
|
|
1
|
+
# 持久提醒
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
使用 Plugin Runtime、持久状态与精确 DeliveryTarget 的个人提醒插件。任务绑定创建者和原会话;群聊其他用户、其他 Endpoint、频道父级或线程不能查看和取消。
|
|
4
|
+
|
|
5
|
+
## 安装
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @zhin.js/command @zhin.js/schedule-feature
|
|
9
|
+
zhin install @zhin.js/plugin-reminder
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
在项目根 `package.json#zhin` 合并以下挂载(保留已有条目):
|
|
13
|
+
|
|
14
|
+
```json
|
|
15
|
+
{
|
|
16
|
+
"features": [{ "package": "@zhin.js/schedule-feature" }],
|
|
17
|
+
"plugins": [
|
|
18
|
+
{ "package": "@zhin.js/plugin-reminder", "instanceKey": "reminder" }
|
|
19
|
+
]
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
根应用从 `zhin.js` 继承 Stable Command Feature;上面的直接 Command 依赖确保子插件解析到同一物理 provider。安装命令会添加插件挂载;手动维护时同样使用上述清单。启用支持持久状态原子更新的 Database Host。缺少持久数据库或精确目标投递 Host 时启动明确失败,不会降级为内存保存。SQLite、PostgreSQL、MySQL 的状态能力由框架 Database Host 提供;本插件回归使用真实 SQLite 文件。
|
|
24
|
+
|
|
25
|
+
```yaml
|
|
26
|
+
plugins:
|
|
27
|
+
reminder:
|
|
28
|
+
timezone: UTC
|
|
29
|
+
maxTasks: 20
|
|
30
|
+
maxTasksPerSender: 10
|
|
31
|
+
batchSize: 5
|
|
32
|
+
missedGraceSeconds: 15
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
配置统一由 Schema 校验;修改仅重载插件。timezone 是有效的 IANA 时区,只影响显示,默认 UTC。绝对时间必须显式包含 `Z` 或 `±hh:mm`,没有隐式本地时间转换。多个实例应显式配置不同的 `commandNamespace`,例如 `personal` 和 `work`;使用时在指令前添加对应命名空间。Endpoint 的 `commandPrefix` 决定指令前缀。
|
|
36
|
+
|
|
37
|
+
## 指令
|
|
38
|
+
|
|
39
|
+
以下示例省略 Endpoint 前缀与插件显式命名空间。各指令的 `--help` 提供参数说明。
|
|
40
|
+
|
|
41
|
+
```text
|
|
42
|
+
remind after 600 十分钟后休息
|
|
43
|
+
remind at 2027-01-01T09:00:00+08:00 新年会议
|
|
44
|
+
remind every 3600 喝水
|
|
45
|
+
remind list
|
|
46
|
+
remind cancel <任务 ID>
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
延迟是 1 秒至一年;周期是 60 秒至一年;内容是 1–500 字符。时间必须在未来一年内。指令权限经过框架 PermissionHost;插件没有额外权限旁路或取消其他发送者任务的管理员接口。
|
|
50
|
+
|
|
51
|
+
## 执行与恢复
|
|
52
|
+
|
|
53
|
+
`schedules/poll/index.ts` 每 5 秒通过当前 owner 的 ScheduleContext 解析服务,没有队列积压时通常在目标时间后 5 秒内扫描;平台投递耗时不计入此保证。服务不保存 Message、客户端或旧 snapshot。周期是固定秒数的间隔,不是墙上时钟的 cron,不受夏令时跳变影响。
|
|
54
|
+
|
|
55
|
+
- 一次性任务:停机错过后恢复时补发至多一次。
|
|
56
|
+
- 周期任务:超过 `missedGraceSeconds` 的轮次跳过,推进至未来,停机后不连发遗漏轮次。
|
|
57
|
+
- 每轮最多投递 `batchSize` 条,逐条执行;同一服务扫描不重叠,持久原子 claim 防止同一任务并发领取。
|
|
58
|
+
- 投递前持久记录为“投递中或未确认”。平台返回非空消息 ID 才记为完成;明确未发送或拒绝会停止任务。未知错误、没有 ID、进程在 claim 后崩溃、投递成功但状态落盘失败都保持未确认,**不自动重试**。用户核对原会话后取消或重新创建。
|
|
59
|
+
- 取消不撤销已开始的外部投递。完成回写不会覆盖在途取消。重启或热重载保留未知状态,避免重复消息。
|
|
60
|
+
|
|
61
|
+
这是保守的至多一次尝试策略,不是 outbox 或 exactly-once 投递。claim 后、实际发送前崩溃可能丢失一次提醒;周期任务遇未知投递也会暂停,需要用户处理。
|
|
62
|
+
|
|
63
|
+
`maxTasks` 限制新建时每实例的活跃任务(待触发或未确认),不是降低配置后立即裁剪的记录总数;状态硬上限为 32 条记录,每发送者在会话内最多 10 条活跃任务;总编码大小限制为 60 KiB。完成或取消记录在新建任务时优先淘汰,未知任务持续占用限额,直到显式取消。减少配置上限不删除既有活跃任务,只限制继续创建。
|
|
64
|
+
|
|
65
|
+
## 显式本地检查
|
|
66
|
+
|
|
67
|
+
插件通过正式 `pluginVerificationToken` 暴露 `local-state` 检查。inspect/checks 只显示副作用,只有用户显式 verify 才写入、读回并删除独立探针键。探针 TTL 为 60 秒。验证被取消或热重载打断时,清理拒绝会使检查明确失败,短 TTL 使残留探针失效;不能将被打断的旧代结果视为当前验证通过。检查不创建可投递任务、不发送消息,仅证明当前 owner 的数据库状态可读写;不证明平台投递或调度健康。
|
|
68
|
+
|
|
69
|
+
## 目录
|
|
70
|
+
|
|
71
|
+
```text
|
|
72
|
+
plugin.ts # Schema、owner 服务资源及显式检查
|
|
73
|
+
src/ # 服务、状态契约与指令 helper
|
|
74
|
+
commands/remind/*/index.ts # 集中参数声明与标准 Command dispatcher
|
|
75
|
+
schedules/poll/index.ts # 正式 Schedule Feature 入口
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Agent 工具与技能
|
|
79
|
+
|
|
80
|
+
启用 `@zhin.js/tool` 与 `@zhin.js/skill` Feature 后,本插件提供技能 `reminder` 与工具:
|
|
81
|
+
|
|
82
|
+
| 工具 | 说明 |
|
|
83
|
+
| ----------------- | ----------------------------------------------------------------- |
|
|
84
|
+
| `reminder_create` | 创建一次性(`after` / `at`)或周期(`every_seconds`)提醒;需批准 |
|
|
85
|
+
| `reminder_list` | 列出当前发送者在当前会话的提醒 |
|
|
86
|
+
| `reminder_cancel` | 按 id 取消;需批准 |
|
|
87
|
+
|
|
88
|
+
与原生 `schedule_add` 的区别:本插件到点**向会话发送提醒文案**;`schedule_*` 到点**再次唤醒 Agent**(可带 cron / 工作日等)。绝对时间仍须带 `Z` 或 `±hh:mm`。
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
|
+
import { defineCommand } from 'zhin.js/command';
|
|
3
|
+
import { reminderServiceToken } from "../../../lib/runtime.js";
|
|
4
|
+
import { reminderActor, replyReminder } from "../../../lib/caller.js";
|
|
5
|
+
import { MAX_DELAY_SECONDS } from "../../../lib/service.js";
|
|
6
|
+
export default defineCommand({
|
|
7
|
+
description: '创建一次性延迟提醒',
|
|
8
|
+
params: { seconds: { type: 'integer', min: 1, max: MAX_DELAY_SECONDS, description: '延迟秒数' }, text: { type: 'text', min: 1, max: 500, description: '提醒内容' } },
|
|
9
|
+
async execute({ input, params, use }) {
|
|
10
|
+
return replyReminder(async () => {
|
|
11
|
+
const service = use(reminderServiceToken);
|
|
12
|
+
const task = await service.add(reminderActor(input), params.text, Date.now() + params.seconds * 1000, null);
|
|
13
|
+
return `已创建提醒\n${service.describe(task)}`;
|
|
14
|
+
});
|
|
15
|
+
},
|
|
16
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { defineCommand } from 'zhin.js/command';
|
|
2
|
+
import { reminderServiceToken } from '../../../src/runtime.js';
|
|
3
|
+
import { reminderActor, replyReminder } from '../../../src/caller.js';
|
|
4
|
+
import { MAX_DELAY_SECONDS } from '../../../src/service.js';
|
|
5
|
+
|
|
6
|
+
export default defineCommand({
|
|
7
|
+
description: '创建一次性延迟提醒',
|
|
8
|
+
params: { seconds: { type: 'integer', min: 1, max: MAX_DELAY_SECONDS, description: '延迟秒数' }, text: { type: 'text', min: 1, max: 500, description: '提醒内容' } },
|
|
9
|
+
async execute({ input, params, use }) {
|
|
10
|
+
return replyReminder(async () => {
|
|
11
|
+
const service = use(reminderServiceToken);
|
|
12
|
+
const task = await service.add(reminderActor(input), params.text, Date.now() + params.seconds * 1000, null);
|
|
13
|
+
return `已创建提醒\n${service.describe(task)}`;
|
|
14
|
+
});
|
|
15
|
+
},
|
|
16
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
|
+
import { defineCommand } from 'zhin.js/command';
|
|
3
|
+
import { reminderServiceToken } from "../../../lib/runtime.js";
|
|
4
|
+
import { reminderActor, replyReminder } from "../../../lib/caller.js";
|
|
5
|
+
import { parseReminderInstant } from "../../../lib/service.js";
|
|
6
|
+
export default defineCommand({
|
|
7
|
+
description: '创建绝对时间提醒',
|
|
8
|
+
params: { time: { type: 'string', description: '包含 Z 或 ±hh:mm 的 ISO 时间', validate: (value) => { try {
|
|
9
|
+
parseReminderInstant(String(value));
|
|
10
|
+
return true;
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return '绝对时间须为有效的 ISO 时间并包含时区 offset';
|
|
14
|
+
} } }, text: { type: 'text', min: 1, max: 500, description: '提醒内容' } },
|
|
15
|
+
async execute({ input, params, use }) {
|
|
16
|
+
return replyReminder(async () => {
|
|
17
|
+
const service = use(reminderServiceToken);
|
|
18
|
+
const task = await service.add(reminderActor(input), params.text, parseReminderInstant(params.time), null);
|
|
19
|
+
return `已创建提醒\n${service.describe(task)}`;
|
|
20
|
+
});
|
|
21
|
+
},
|
|
22
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { defineCommand, type CommandParameterValue } from 'zhin.js/command';
|
|
2
|
+
import { reminderServiceToken } from '../../../src/runtime.js';
|
|
3
|
+
import { reminderActor, replyReminder } from '../../../src/caller.js';
|
|
4
|
+
import { parseReminderInstant } from '../../../src/service.js';
|
|
5
|
+
|
|
6
|
+
export default defineCommand({
|
|
7
|
+
description: '创建绝对时间提醒',
|
|
8
|
+
params: { time: { type: 'string', description: '包含 Z 或 ±hh:mm 的 ISO 时间', validate: (value: CommandParameterValue) => { try { parseReminderInstant(String(value)); return true; } catch { return '绝对时间须为有效的 ISO 时间并包含时区 offset'; } } }, text: { type: 'text', min: 1, max: 500, description: '提醒内容' } },
|
|
9
|
+
async execute({ input, params, use }) {
|
|
10
|
+
return replyReminder(async () => {
|
|
11
|
+
const service = use(reminderServiceToken);
|
|
12
|
+
const task = await service.add(reminderActor(input), params.text, parseReminderInstant(params.time), null);
|
|
13
|
+
return `已创建提醒\n${service.describe(task)}`;
|
|
14
|
+
});
|
|
15
|
+
},
|
|
16
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
|
+
import { defineCommand } from 'zhin.js/command';
|
|
3
|
+
import { reminderServiceToken } from "../../../lib/runtime.js";
|
|
4
|
+
import { reminderActor, replyReminder } from "../../../lib/caller.js";
|
|
5
|
+
export default defineCommand({
|
|
6
|
+
description: '取消本人在当前会话的提醒',
|
|
7
|
+
params: { id: { type: 'string', description: 'remind list 中的任务 ID' } },
|
|
8
|
+
async execute({ input, params, use }) {
|
|
9
|
+
return replyReminder(async () => await use(reminderServiceToken).cancel(reminderActor(input), params.id) ? '已取消提醒;已经开始的投递无法撤销' : '未找到你在此会话的提醒');
|
|
10
|
+
},
|
|
11
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { defineCommand } from 'zhin.js/command';
|
|
2
|
+
import { reminderServiceToken } from '../../../src/runtime.js';
|
|
3
|
+
import { reminderActor, replyReminder } from '../../../src/caller.js';
|
|
4
|
+
|
|
5
|
+
export default defineCommand({
|
|
6
|
+
description: '取消本人在当前会话的提醒',
|
|
7
|
+
params: { id: { type: 'string', description: 'remind list 中的任务 ID' } },
|
|
8
|
+
async execute({ input, params, use }) {
|
|
9
|
+
return replyReminder(async () => await use(reminderServiceToken).cancel(reminderActor(input), params.id) ? '已取消提醒;已经开始的投递无法撤销' : '未找到你在此会话的提醒');
|
|
10
|
+
},
|
|
11
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
|
+
import { defineCommand } from 'zhin.js/command';
|
|
3
|
+
import { reminderServiceToken } from "../../../lib/runtime.js";
|
|
4
|
+
import { reminderActor, replyReminder } from "../../../lib/caller.js";
|
|
5
|
+
import { MAX_DELAY_SECONDS } from "../../../lib/service.js";
|
|
6
|
+
export default defineCommand({
|
|
7
|
+
description: '创建固定周期提醒',
|
|
8
|
+
params: { seconds: { type: 'integer', min: 60, max: MAX_DELAY_SECONDS, description: '周期秒数(至少 60)' }, text: { type: 'text', min: 1, max: 500, description: '提醒内容' } },
|
|
9
|
+
async execute({ input, params, use }) {
|
|
10
|
+
return replyReminder(async () => {
|
|
11
|
+
const service = use(reminderServiceToken);
|
|
12
|
+
const task = await service.add(reminderActor(input), params.text, Date.now() + params.seconds * 1000, params.seconds * 1000);
|
|
13
|
+
return `已创建周期提醒\n${service.describe(task)}`;
|
|
14
|
+
});
|
|
15
|
+
},
|
|
16
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { defineCommand } from 'zhin.js/command';
|
|
2
|
+
import { reminderServiceToken } from '../../../src/runtime.js';
|
|
3
|
+
import { reminderActor, replyReminder } from '../../../src/caller.js';
|
|
4
|
+
import { MAX_DELAY_SECONDS } from '../../../src/service.js';
|
|
5
|
+
|
|
6
|
+
export default defineCommand({
|
|
7
|
+
description: '创建固定周期提醒',
|
|
8
|
+
params: { seconds: { type: 'integer', min: 60, max: MAX_DELAY_SECONDS, description: '周期秒数(至少 60)' }, text: { type: 'text', min: 1, max: 500, description: '提醒内容' } },
|
|
9
|
+
async execute({ input, params, use }) {
|
|
10
|
+
return replyReminder(async () => {
|
|
11
|
+
const service = use(reminderServiceToken);
|
|
12
|
+
const task = await service.add(reminderActor(input), params.text, Date.now() + params.seconds * 1000, params.seconds * 1000);
|
|
13
|
+
return `已创建周期提醒\n${service.describe(task)}`;
|
|
14
|
+
});
|
|
15
|
+
},
|
|
16
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Generated by build-plugin-runtime-entries.mjs. Do not edit.
|
|
2
|
+
import { defineCommand } from 'zhin.js/command';
|
|
3
|
+
import { reminderServiceToken } from "../../../lib/runtime.js";
|
|
4
|
+
import { reminderActor, replyReminder } from "../../../lib/caller.js";
|
|
5
|
+
export default defineCommand({
|
|
6
|
+
description: '查看本人在当前会话的提醒',
|
|
7
|
+
async execute({ input, use }) {
|
|
8
|
+
return replyReminder(async () => {
|
|
9
|
+
const service = use(reminderServiceToken);
|
|
10
|
+
const tasks = await service.list(reminderActor(input));
|
|
11
|
+
return tasks.length ? tasks.map(task => service.describe(task)).join('\n\n') : '你在此会话没有提醒任务';
|
|
12
|
+
});
|
|
13
|
+
},
|
|
14
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { defineCommand } from 'zhin.js/command';
|
|
2
|
+
import { reminderServiceToken } from '../../../src/runtime.js';
|
|
3
|
+
import { reminderActor, replyReminder } from '../../../src/caller.js';
|
|
4
|
+
|
|
5
|
+
export default defineCommand({
|
|
6
|
+
description: '查看本人在当前会话的提醒',
|
|
7
|
+
async execute({ input, use }) {
|
|
8
|
+
return replyReminder(async () => {
|
|
9
|
+
const service = use(reminderServiceToken);
|
|
10
|
+
const tasks = await service.list(reminderActor(input));
|
|
11
|
+
return tasks.length ? tasks.map(task => service.describe(task)).join('\n\n') : '你在此会话没有提醒任务';
|
|
12
|
+
});
|
|
13
|
+
},
|
|
14
|
+
});
|
package/lib/caller.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { CommandMessage } from '@zhin.js/command';
|
|
2
|
+
import { type ReminderActor } from './service.js';
|
|
3
|
+
export declare function reminderActor(input: CommandMessage | undefined): ReminderActor;
|
|
4
|
+
export declare function replyReminder(operation: () => Promise<string>): Promise<string>;
|
|
5
|
+
//# sourceMappingURL=caller.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"caller.d.ts","sourceRoot":"","sources":["../src/caller.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAsB,KAAK,aAAa,EAAE,MAAM,cAAc,CAAC;AAEtE,wBAAgB,aAAa,CAAC,KAAK,EAAE,cAAc,GAAG,SAAS,GAAG,aAAa,CAG9E;AAED,wBAAsB,aAAa,CAAC,SAAS,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAGrF"}
|
package/lib/caller.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { createDeliveryTarget } from 'zhin.js';
|
|
2
|
+
import { ReminderInputError } from './service.js';
|
|
3
|
+
export function reminderActor(input) {
|
|
4
|
+
if (!input?.sender?.id)
|
|
5
|
+
throw new ReminderInputError('提醒指令需要真实会话和发送者');
|
|
6
|
+
return { senderId: input.sender.id, target: createDeliveryTarget(input) };
|
|
7
|
+
}
|
|
8
|
+
export async function replyReminder(operation) {
|
|
9
|
+
try {
|
|
10
|
+
return await operation();
|
|
11
|
+
}
|
|
12
|
+
catch (error) {
|
|
13
|
+
if (error instanceof ReminderInputError)
|
|
14
|
+
return error.message;
|
|
15
|
+
throw error;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=caller.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"caller.js","sourceRoot":"","sources":["../src/caller.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAE/C,OAAO,EAAE,kBAAkB,EAAsB,MAAM,cAAc,CAAC;AAEtE,MAAM,UAAU,aAAa,CAAC,KAAiC;IAC7D,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE;QAAE,MAAM,IAAI,kBAAkB,CAAC,gBAAgB,CAAC,CAAC;IACvE,OAAO,EAAE,QAAQ,EAAE,KAAK,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,oBAAoB,CAAC,KAAK,CAAC,EAAE,CAAC;AAC5E,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,SAAgC;IAClE,IAAI,CAAC;QAAC,OAAO,MAAM,SAAS,EAAE,CAAC;IAAC,CAAC;IACjC,OAAO,KAAK,EAAE,CAAC;QAAC,IAAI,KAAK,YAAY,kBAAkB;YAAE,OAAO,KAAK,CAAC,OAAO,CAAC;QAAC,MAAM,KAAK,CAAC;IAAC,CAAC;AAC/F,CAAC"}
|
package/lib/config.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Schema } from 'zhin.js';
|
|
2
|
+
export declare const reminderConfig: Schema<Schema.InputRecord<{
|
|
3
|
+
timezone: Schema<string, string> & Schema.Present;
|
|
4
|
+
maxTasks: Schema<number, number> & Schema.Present;
|
|
5
|
+
maxTasksPerSender: Schema<number, number> & Schema.Present;
|
|
6
|
+
batchSize: Schema<number, number> & Schema.Present;
|
|
7
|
+
missedGraceSeconds: Schema<number, number> & Schema.Present;
|
|
8
|
+
}>, {
|
|
9
|
+
timezone: string;
|
|
10
|
+
maxTasks: number;
|
|
11
|
+
maxTasksPerSender: number;
|
|
12
|
+
batchSize: number;
|
|
13
|
+
missedGraceSeconds: number;
|
|
14
|
+
} & {}>;
|
|
15
|
+
export type ReminderConfig = Schema.Output<typeof reminderConfig>;
|
|
16
|
+
export declare function assertReminderTimezone(timezone: string): void;
|
|
17
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEjC,eAAO,MAAM,cAAc;;;;;;;;;;;;OAMzB,CAAC;AAEH,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,cAAc,CAAC,CAAC;AAElE,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAG7D"}
|
package/lib/config.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Schema } from 'zhin.js';
|
|
2
|
+
export const reminderConfig = Schema.object({
|
|
3
|
+
timezone: Schema.string().default('UTC').description('显示提醒时间的 IANA 时区;绝对时间仍必须提供 ISO offset').effect('reload'),
|
|
4
|
+
maxTasks: Schema.number().min(1).max(32).step(1).default(20).description('每个插件实例新建时允许的活跃任务上限(待触发或未确认);降低上限不删除既有任务').effect('reload'),
|
|
5
|
+
maxTasksPerSender: Schema.number().min(1).max(10).step(1).default(10).description('同一发送者在同一会话的任务上限').effect('reload'),
|
|
6
|
+
batchSize: Schema.number().min(1).max(5).step(1).default(5).description('每次扫描最多投递的任务数').effect('reload'),
|
|
7
|
+
missedGraceSeconds: Schema.number().min(5).max(60).step(1).default(15).description('周期轮次超过此延迟后跳过;停机期间不补历史轮次').effect('reload'),
|
|
8
|
+
});
|
|
9
|
+
export function assertReminderTimezone(timezone) {
|
|
10
|
+
try {
|
|
11
|
+
new Intl.DateTimeFormat('zh-CN', { timeZone: timezone }).format(0);
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
throw new TypeError(`提醒 timezone 必须是有效的 IANA 时区:${timezone}`);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEjC,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,CAAC;IAC1C,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,WAAW,CAAC,sCAAsC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC;IAC7G,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,WAAW,CAAC,yCAAyC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC;IACpI,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,WAAW,CAAC,iBAAiB,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC;IACrH,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,cAAc,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC;IACxG,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,WAAW,CAAC,yBAAyB,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC;CAC/H,CAAC,CAAC;AAIH,MAAM,UAAU,sBAAsB,CAAC,QAAgB;IACrD,IAAI,CAAC;QAAC,IAAI,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IAAC,CAAC;IAC3E,MAAM,CAAC;QAAC,MAAM,IAAI,SAAS,CAAC,8BAA8B,QAAQ,EAAE,CAAC,CAAC;IAAC,CAAC;AAC1E,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"duration.d.ts","sourceRoot":"","sources":["../src/duration.ts"],"names":[],"mappings":"AAGA,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CA2B1D"}
|
package/lib/duration.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** Relative durations for Agent tools: `10m`, `2h`, `1d`, or plain seconds. */
|
|
2
|
+
import { ReminderInputError } from "./service.js";
|
|
3
|
+
export function parseRelativeDelayMs(value) {
|
|
4
|
+
const text = value.trim().toLowerCase();
|
|
5
|
+
if (/^\d+$/.test(text)) {
|
|
6
|
+
const seconds = Number(text);
|
|
7
|
+
if (!Number.isSafeInteger(seconds) || seconds < 1) {
|
|
8
|
+
throw new ReminderInputError("延迟秒数无效");
|
|
9
|
+
}
|
|
10
|
+
return seconds * 1000;
|
|
11
|
+
}
|
|
12
|
+
const match = /^(\d+)\s*(s|m|h|d|秒|分|分钟|小时|天)$/u.exec(text);
|
|
13
|
+
if (!match) {
|
|
14
|
+
throw new ReminderInputError("时长格式无效(示例:600、10m、2h、1d)");
|
|
15
|
+
}
|
|
16
|
+
const amount = Number(match[1]);
|
|
17
|
+
const unit = match[2];
|
|
18
|
+
const factor = {
|
|
19
|
+
s: 1000,
|
|
20
|
+
秒: 1000,
|
|
21
|
+
m: 60_000,
|
|
22
|
+
分: 60_000,
|
|
23
|
+
分钟: 60_000,
|
|
24
|
+
h: 3_600_000,
|
|
25
|
+
小时: 3_600_000,
|
|
26
|
+
d: 86_400_000,
|
|
27
|
+
天: 86_400_000,
|
|
28
|
+
}[unit];
|
|
29
|
+
return amount * factor;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=duration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"duration.js","sourceRoot":"","sources":["../src/duration.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAElD,MAAM,UAAU,oBAAoB,CAAC,KAAa;IAChD,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACxC,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACvB,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;QAC7B,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;YAClD,MAAM,IAAI,kBAAkB,CAAC,QAAQ,CAAC,CAAC;QACzC,CAAC;QACD,OAAO,OAAO,GAAG,IAAI,CAAC;IACxB,CAAC;IACD,MAAM,KAAK,GAAG,kCAAkC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC5D,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,kBAAkB,CAAC,0BAA0B,CAAC,CAAC;IAC3D,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAChC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAE,CAAC;IACvB,MAAM,MAAM,GAAG;QACb,CAAC,EAAE,IAAI;QACP,CAAC,EAAE,IAAI;QACP,CAAC,EAAE,MAAM;QACT,CAAC,EAAE,MAAM;QACT,EAAE,EAAE,MAAM;QACV,CAAC,EAAE,SAAS;QACZ,EAAE,EAAE,SAAS;QACb,CAAC,EAAE,UAAU;QACb,CAAC,EAAE,UAAU;KACd,CAAC,IAAI,CAAE,CAAC;IACT,OAAO,MAAM,GAAG,MAAM,CAAC;AACzB,CAAC"}
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { reminderConfig, type ReminderConfig } from "./config.js";
|
|
2
|
+
export { reminderServiceToken } from "./runtime.js";
|
|
3
|
+
export { reminderActorFromTool, toolDeliveryTarget } from "./tool-actor.js";
|
|
4
|
+
export { parseRelativeDelayMs } from "./duration.js";
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAC5E,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC"}
|
package/lib/index.js
ADDED
package/lib/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAuB,MAAM,aAAa,CAAC;AAClE,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAC5E,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC"}
|
package/lib/runtime.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAEpD,eAAO,MAAM,oBAAoB,0CAA6F,CAAC"}
|
package/lib/runtime.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAGtC,MAAM,CAAC,MAAM,oBAAoB,GAAG,WAAW,CAAkB,uBAAuB,EAAE,mCAAmC,CAAC,CAAC"}
|
package/lib/service.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { type DeliveryTarget, type OutboundHost, type PluginStateStore } from 'zhin.js';
|
|
2
|
+
import type { ReminderConfig } from './config.js';
|
|
3
|
+
export declare const REMINDER_STATE_KEY = "reminders/v1";
|
|
4
|
+
export declare const MAX_DELAY_SECONDS: number;
|
|
5
|
+
export declare class ReminderInputError extends Error {
|
|
6
|
+
}
|
|
7
|
+
type TaskStatus = 'pending' | 'unknown' | 'delivered' | 'failed' | 'cancelled';
|
|
8
|
+
export type ReminderActor = {
|
|
9
|
+
readonly senderId: string;
|
|
10
|
+
readonly target: DeliveryTarget;
|
|
11
|
+
};
|
|
12
|
+
export type ReminderTask = {
|
|
13
|
+
readonly id: string;
|
|
14
|
+
readonly senderId: string;
|
|
15
|
+
readonly target: DeliveryTarget;
|
|
16
|
+
readonly text: string;
|
|
17
|
+
readonly createdAt: number;
|
|
18
|
+
readonly nextRunAt: number;
|
|
19
|
+
readonly intervalMs: number | null;
|
|
20
|
+
readonly status: TaskStatus;
|
|
21
|
+
readonly claimId: string | null;
|
|
22
|
+
readonly lastRunAt: number | null;
|
|
23
|
+
readonly detail: string;
|
|
24
|
+
};
|
|
25
|
+
export type ReminderBook = {
|
|
26
|
+
readonly version: 1;
|
|
27
|
+
readonly tasks: readonly ReminderTask[];
|
|
28
|
+
};
|
|
29
|
+
/** Generation-owned worker; saved state contains plain identities, never live objects. */
|
|
30
|
+
export declare class ReminderService {
|
|
31
|
+
#private;
|
|
32
|
+
constructor(state: PluginStateStore, outbound: OutboundHost, config: ReminderConfig);
|
|
33
|
+
stop(): void;
|
|
34
|
+
add(actor: ReminderActor, text: string, at: number, intervalMs: number | null): Promise<ReminderTask>;
|
|
35
|
+
list(actor: ReminderActor): Promise<readonly ReminderTask[]>;
|
|
36
|
+
cancel(actor: ReminderActor, id: string): Promise<boolean>;
|
|
37
|
+
tick(): Promise<void>;
|
|
38
|
+
describe(task: ReminderTask): string;
|
|
39
|
+
}
|
|
40
|
+
export declare function parseReminderInstant(value: string): number;
|
|
41
|
+
export {};
|
|
42
|
+
//# sourceMappingURL=service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AACA,OAAO,EAAwB,KAAK,cAAc,EAAE,KAAK,YAAY,EAAE,KAAK,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAE9G,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,eAAO,MAAM,kBAAkB,iBAAiB,CAAC;AACjD,eAAO,MAAM,iBAAiB,QAAqB,CAAC;AACpD,qBAAa,kBAAmB,SAAQ,KAAK;CAAG;AAEhD,KAAK,UAAU,GAAG,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,QAAQ,GAAG,WAAW,CAAC;AAC/E,MAAM,MAAM,aAAa,GAAG;IAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAA;CAAE,CAAC;AAC3F,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;IAChF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC9E,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAChE,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CAC7F,CAAC;AACF,MAAM,MAAM,YAAY,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,SAAS,YAAY,EAAE,CAAA;CAAE,CAAC;AAE5F,0FAA0F;AAC1F,qBAAa,eAAe;;gBAOd,KAAK,EAAE,gBAAgB,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,EAAE,cAAc;IAMnF,IAAI,IAAI,IAAI;IAEN,GAAG,CAAC,KAAK,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,YAAY,CAAC;IAuBrG,IAAI,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,SAAS,YAAY,EAAE,CAAC;IAM5D,MAAM,CAAC,KAAK,EAAE,aAAa,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAU1D,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IA0C3B,QAAQ,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM;CAQrC;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAU1D"}
|