@zhin.js/process-monitor 1.1.0 → 1.1.2

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 CHANGED
@@ -1,274 +1,53 @@
1
1
  # @zhin.js/process-monitor
2
2
 
3
- 进程监控与重启通知插件,自动记录机器人重启情况并通知管理员。
3
+ Zhin Plugin Runtime 的进程启动检测与状态查询插件。
4
4
 
5
- ## 功能特性
5
+ ## 能力
6
6
 
7
- - ✅ **智能检测**:自动识别启动类型(首次启动/正常重启/异常崩溃)
8
- - ✅ **历史记录**:数据库持久化存储重启历史
9
- - ✅ **多渠道通知**:支持用户私聊、群组消息、邮件、Webhook
10
- - ✅ **统计分析**:重启次数、崩溃次数、累计运行时长
11
- - ✅ **健康检查**:定期检查进程状态(内存、运行时长)
12
- - ✅ **命令管理**:查看状态、历史记录、重置统计
7
+ - 通过 `data/process-state.json` 记录启动 PID、启动时间、正常退出标记、重启次数和崩溃次数。
8
+ - `process-status` Command 返回当前 PID、运行时长、内存和累计统计;Agent Tool 位于 `skills/process-monitor/tools/process-status/index.ts`,只在 Skill 激活后披露。
9
+ - 可在首次启动、正常重启或异常崩溃后调用 HTTP Webhook。
10
+ - 每个插件 owner 独立持有 `ProcessMonitor` 实例、状态和信号监听器;generation dispose 时自动清理监听器。
13
11
 
14
- ## 安装
15
-
16
- ### 方式一:从 npm 安装(发布后)
12
+ ## 安装与配置
17
13
 
18
14
  ```bash
19
15
  pnpm add @zhin.js/process-monitor
20
16
  ```
21
17
 
22
- ### 方式二:本地开发
23
-
24
- 如果你在 Zhin 项目中开发,插件已经在 workspace 中,直接在配置文件中启用即可:
25
-
26
- ```yaml
27
- plugins:
28
- - "@zhin.js/process-monitor"
29
- ```
30
-
31
- ## 配置
32
-
33
- 在 `zhin.config.yml` 中配置:
34
-
35
18
  ```yaml
36
19
  plugins:
37
- - "@zhin.js/process-monitor"
38
-
39
- process-monitor:
40
- enabled: true # 是否启用
41
- recordHistory: true # 是否记录历史
42
- maxHistoryRecords: 100 # 最大历史记录数
43
- checkInterval: 60000 # 健康检查间隔(毫秒)
44
- notifyOnStart: true # 启动时通知
45
- notifyOnRestart: true # 重启时通知
46
- notifyOnCrash: true # 崩溃时通知
47
-
48
- notifyChannels:
49
- # 方式一:发送给指定用户(私聊)
50
- - type: user
51
- target: "123456789" # 用户 ID
52
- platform: "icqq" # 平台标识(可选,默认第一个适配器)
53
-
54
- # 方式二:发送到指定群组
55
- - type: group
56
- target: "987654321" # 群组 ID
57
- platform: "icqq"
58
-
59
- # 方式三:发送邮件(需配置邮件服务)
60
- - type: email
61
- target: "admin@example.com"
62
-
63
- # 方式四:Webhook 回调
64
- - type: webhook
65
- target: "https://your-webhook.com/notify"
66
- ```
67
-
68
- ## 使用
69
-
70
- ### 查看状态
71
-
72
- ```bash
73
- monitor --status
74
- # 或
75
- pm -s
76
- ```
77
-
78
- 输出示例:
79
- ```
80
- 📊 进程监控状态
81
-
82
- 🚀 当前 PID: 12345
83
- ⏱️ 运行时长: 2天5小时30分钟
84
- 💾 内存使用: 156 MB
85
- 🔄 总重启: 3 次
86
- 💥 崩溃: 1 次
87
- 📈 累计运行: 15天8小时
88
- 🖥️ 主机: my-server
89
- 💻 平台: linux-x64
90
- 📦 Node: v23.7.0
91
- ```
92
-
93
- ### 查看重启历史
94
-
95
- ```bash
96
- monitor --history # 最近 10 条
97
- monitor --history 20 # 最近 20 条
98
- ```
99
-
100
- 输出示例:
101
- ```
102
- 📜 重启历史记录
103
-
104
- 🔄 2026-02-05 14:30:25
105
- 原因: restart | PID: 12345
106
- 运行: 2天5小时30分钟
107
-
108
- 💥 2026-02-03 09:15:10
109
- 原因: crash | PID: 11234
110
- 运行: 3小时45分钟
111
-
112
- 🚀 2026-02-03 05:30:00
113
- 原因: start | PID: 11234
114
- ```
115
-
116
- ### 重置统计
117
-
118
- ```bash
119
- monitor --reset
120
- ```
121
-
122
- ## 通知示例
123
-
124
- ### 启动通知
125
-
20
+ process-monitor:
21
+ package: "@zhin.js/process-monitor"
22
+ config:
23
+ enabled: true
24
+ notifyOnStart: true
25
+ notifyOnRestart: true
26
+ notifyOnCrash: true
27
+ notifyChannels:
28
+ - type: webhook
29
+ target: "${PROCESS_MONITOR_WEBHOOK_URL}"
126
30
  ```
127
- 🚀 【进程监控通知】
128
31
 
129
- 📊 事件: 首次启动
130
- ⏰ 时间: 2026-02-05 14:30:25
131
- 🖥️ 主机: my-server
132
- 🔢 PID: 12345
133
- 💻 平台: linux-x64
134
- 📦 Node: v23.7.0
135
- 💾 内存: 120 MB
136
-
137
- 📈 统计:
138
- • 总重启: 0 次
139
- • 崩溃: 0 次
140
- • 累计运行: 0秒
141
- ```
142
-
143
- ### 崩溃通知
144
-
145
- ```
146
- 💥 【进程监控通知】
147
-
148
- 📊 事件: 异常崩溃
149
- ⏰ 时间: 2026-02-05 18:45:30
150
- 🖥️ 主机: my-server
151
- 🔢 PID: 12346
152
- 💻 平台: linux-x64
153
- 📦 Node: v23.7.0
154
- ⏱️ 运行时长: 4小时15分钟
155
- 💾 内存: 256 MB
156
-
157
- 📈 统计:
158
- • 总重启: 3 次
159
- • 崩溃: 1 次
160
- • 累计运行: 15天8小时
161
- ```
162
-
163
- ## 工作原理
164
-
165
- ### 启动检测
166
-
167
- 1. **首次启动**:无历史状态文件
168
- 2. **正常重启**:距上次启动 > 5 分钟
169
- 3. **异常崩溃**:距上次启动 < 5 分钟
170
-
171
- ### 数据持久化
172
-
173
- - **状态文件**:`data/process-state.json`
174
- - 记录上次 PID、启动时间、统计数据
175
-
176
- - **数据库表**:`process_restart_records`
177
- - 完整的重启历史记录
178
-
179
- ### 通知流程
180
-
181
- ```
182
- 进程启动 → 检测启动原因 → 记录数据库 → 判断是否通知 → 发送通知
183
- ```
184
-
185
- ## Webhook 数据格式
186
-
187
- POST 请求体:
32
+ `notifyChannels` 当前只接受 `webhook`。Webhook 使用 `POST application/json`,正文包含:
188
33
 
189
34
  ```json
190
35
  {
191
36
  "event": "process_restart",
192
37
  "data": {
193
- "timestamp": "2026-02-05T14:30:25.000Z",
194
- "reason": "crash",
195
- "exitCode": 1,
196
- "uptime": 15300000,
197
- "pid": 12346,
198
- "hostname": "my-server",
38
+ "reason": "restart",
39
+ "timestamp": "2026-09-19T10:00:00.000Z",
40
+ "pid": 12345,
41
+ "hostname": "bot-host",
199
42
  "platform": "linux-x64",
200
- "nodeVersion": "v23.7.0",
201
- "memory": 256
43
+ "nodeVersion": "v24.0.0"
202
44
  },
203
45
  "stats": {
204
- "restartCount": 3,
205
- "crashCount": 1,
206
- "totalUptime": 1324800000
46
+ "restartCount": 1,
47
+ "crashCount": 0,
48
+ "totalUptime": 3600000
207
49
  }
208
50
  }
209
51
  ```
210
52
 
211
- ## 最佳实践
212
-
213
- ### 生产环境配置
214
-
215
- ```yaml
216
- process-monitor:
217
- enabled: true
218
- recordHistory: true
219
- maxHistoryRecords: 200
220
- checkInterval: 300000 # 5 分钟检查一次
221
- notifyOnStart: true
222
- notifyOnRestart: true
223
- notifyOnCrash: true
224
-
225
- notifyChannels:
226
- # 主管理员(私聊)
227
- - type: user
228
- target: "${ADMIN_USER_ID}"
229
- platform: "icqq"
230
-
231
- # 运维群组
232
- - type: group
233
- target: "${OPS_GROUP_ID}"
234
- platform: "icqq"
235
-
236
- # 监控系统 Webhook
237
- - type: webhook
238
- target: "${MONITORING_WEBHOOK_URL}"
239
- ```
240
-
241
- ### 配合系统服务
242
-
243
- 与 systemd/launchd/NSSM 配合使用,实现:
244
- - **系统级监督**:守护进程崩溃自动重启
245
- - **应用级通知**:插件记录并通知管理员
246
-
247
- ### 告警规则建议
248
-
249
- - 🟢 **正常重启**:记录即可,可选通知
250
- - 🟡 **频繁重启**:1小时内重启 > 3 次,需关注
251
- - 🔴 **持续崩溃**:5分钟内崩溃 > 2 次,立即处理
252
-
253
- ## 故障排查
254
-
255
- ### 通知未发送
256
-
257
- 1. 检查配置:`notifyChannels` 是否正确
258
- 2. 检查权限:机器人是否有发送消息权限
259
- 3. 查看日志:是否有错误信息
260
-
261
- ### 历史记录丢失
262
-
263
- 1. 检查数据库连接
264
- 2. 确认 `recordHistory: true`
265
- 3. 查看 `data/process-state.json` 是否存在
266
-
267
- ### 启动类型误判
268
-
269
- - 调整判断阈值(默认 5 分钟)
270
- - 检查系统时间是否正确
271
-
272
- ## 许可证
273
-
274
- MIT License
53
+ 首次没有历史状态时记为 `start`;上次收到 `SIGTERM`/`SIGINT` 时记为 `restart`;未正常退出且五分钟内重新启动时记为 `crash`。
@@ -1,9 +1,9 @@
1
1
  // Generated by build-plugin-runtime-entries.mjs. Do not edit.
2
2
  import { defineCommand } from 'zhin.js/command';
3
- import { formatProcessStatus } from "../lib/monitor.js";
3
+ import { processMonitorToken } from "../../lib/monitor.js";
4
4
  export default defineCommand({
5
5
  description: '查看进程监控状态(PID、运行时长、重启统计)',
6
- execute() {
7
- return formatProcessStatus();
6
+ execute(context) {
7
+ return context.use(processMonitorToken).formatStatus();
8
8
  },
9
9
  });
@@ -1,9 +1,9 @@
1
1
  import { defineCommand } from 'zhin.js/command';
2
- import { formatProcessStatus, type ProcessMonitorConfig } from '../src/monitor.js';
2
+ import { processMonitorToken, type ProcessMonitorConfig } from '../../src/monitor.js';
3
3
 
4
4
  export default defineCommand<ProcessMonitorConfig>({
5
5
  description: '查看进程监控状态(PID、运行时长、重启统计)',
6
- execute() {
7
- return formatProcessStatus();
6
+ execute(context) {
7
+ return context.use(processMonitorToken).formatStatus();
8
8
  },
9
9
  });
package/lib/index.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- export { classifyStartup, formatProcessStatus, formatUptime, processState, resetProcessMonitorForTests, resolveProcessMonitorConfig, startProcessMonitor, startTime, } from './monitor.js';
2
- export type { NotifyChannel, ProcessMonitorConfig, ProcessState, StartupReason } from './monitor.js';
1
+ export { classifyStartup, formatUptime, ProcessMonitor, processMonitorToken, resolveProcessMonitorConfig, } from './monitor.js';
2
+ export type { NotifyChannel, ProcessMonitorConfig, ProcessMonitorOptions, ProcessState, ResolvedProcessMonitorConfig, StartupReason, } from './monitor.js';
3
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,YAAY,EACZ,YAAY,EACZ,2BAA2B,EAC3B,2BAA2B,EAC3B,mBAAmB,EACnB,SAAS,GACV,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,aAAa,EAAE,oBAAoB,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,YAAY,EACZ,cAAc,EACd,mBAAmB,EACnB,2BAA2B,GAC5B,MAAM,cAAc,CAAC;AACtB,YAAY,EACV,aAAa,EACb,oBAAoB,EACpB,qBAAqB,EACrB,YAAY,EACZ,4BAA4B,EAC5B,aAAa,GACd,MAAM,cAAc,CAAC"}
package/lib/index.js CHANGED
@@ -1,2 +1,2 @@
1
- export { classifyStartup, formatProcessStatus, formatUptime, processState, resetProcessMonitorForTests, resolveProcessMonitorConfig, startProcessMonitor, startTime, } from './monitor.js';
1
+ export { classifyStartup, formatUptime, ProcessMonitor, processMonitorToken, resolveProcessMonitorConfig, } from './monitor.js';
2
2
  //# sourceMappingURL=index.js.map
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,YAAY,EACZ,YAAY,EACZ,2BAA2B,EAC3B,2BAA2B,EAC3B,mBAAmB,EACnB,SAAS,GACV,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,YAAY,EACZ,cAAc,EACd,mBAAmB,EACnB,2BAA2B,GAC5B,MAAM,cAAc,CAAC"}
package/lib/monitor.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  export interface NotifyChannel {
2
- type: 'user' | 'group' | 'webhook';
2
+ type: 'webhook';
3
3
  target: string;
4
- platform?: string;
5
4
  }
6
5
  export interface ProcessMonitorConfig {
7
6
  enabled?: boolean;
@@ -13,31 +12,39 @@ export interface ProcessMonitorConfig {
13
12
  export interface ProcessState {
14
13
  lastPid?: number;
15
14
  lastStartTime?: number;
16
- /** SIGTERM/SIGINT 正常退出时置 true,下次启动据此归为 restart 而非 crash。 */
17
15
  cleanExit?: boolean;
18
16
  restartCount: number;
19
17
  crashCount: number;
20
18
  totalUptime: number;
21
19
  }
22
- export declare let processState: ProcessState;
23
- export declare const startTime: number;
24
- export declare function resolveProcessMonitorConfig(raw: ProcessMonitorConfig | undefined): Required<Pick<ProcessMonitorConfig, 'enabled' | 'notifyOnStart' | 'notifyOnRestart' | 'notifyOnCrash'>> & {
20
+ export type StartupReason = 'start' | 'restart' | 'crash';
21
+ export type ResolvedProcessMonitorConfig = ReturnType<typeof resolveProcessMonitorConfig>;
22
+ export declare const processMonitorToken: import("zhin.js").Token<ProcessMonitor>;
23
+ export declare function resolveProcessMonitorConfig(raw: ProcessMonitorConfig | undefined): {
24
+ enabled: boolean;
25
25
  notifyChannels: NotifyChannel[];
26
+ notifyOnStart: boolean;
27
+ notifyOnRestart: boolean;
28
+ notifyOnCrash: boolean;
26
29
  };
27
30
  export declare function formatUptime(ms: number): string;
28
- export declare function formatProcessStatus(): string;
29
- export type StartupReason = 'start' | 'restart' | 'crash';
30
- /**
31
- * Classify this startup from persisted state.
32
- * - 同 PID(热重载,进程未退出)跳过判定,不算 restart/crash。
33
- * - 上次为 SIGTERM/SIGINT 正常退出(cleanExit)时归为 restart,即使间隔 <5min。
34
- */
35
31
  export declare function classifyStartup(state: ProcessState, currentPid: number, now?: number): {
36
32
  reason: StartupReason;
37
33
  uptime?: number;
38
34
  };
39
- /** Start monitoring once; returns disposer for lifecycle cleanup. */
40
- export declare function startProcessMonitor(rawConfig?: ProcessMonitorConfig): () => void;
41
- /** Test helper: reset module state without touching disk. */
42
- export declare function resetProcessMonitorForTests(): void;
35
+ export interface ProcessMonitorOptions {
36
+ readonly stateFile?: string;
37
+ readonly now?: () => number;
38
+ readonly fetch?: typeof fetch;
39
+ }
40
+ /** One plugin owner's process statistics, persistence, notifications, and signal lifecycle. */
41
+ export declare class ProcessMonitor {
42
+ #private;
43
+ constructor(config?: ProcessMonitorConfig, options?: ProcessMonitorOptions);
44
+ get state(): Readonly<ProcessState>;
45
+ get started(): boolean;
46
+ start(): void;
47
+ dispose(): void;
48
+ formatStatus(): string;
49
+ }
43
50
  //# sourceMappingURL=monitor.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"monitor.d.ts","sourceRoot":"","sources":["../src/monitor.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,cAAc,CAAC,EAAE,aAAa,EAAE,CAAC;IACjC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,4DAA4D;IAC5D,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;CACrB;AAID,eAAO,IAAI,YAAY,EAAE,YAIxB,CAAC;AAEF,eAAO,MAAM,SAAS,QAAa,CAAC;AAKpC,wBAAgB,2BAA2B,CACzC,GAAG,EAAE,oBAAoB,GAAG,SAAS,GACpC,QAAQ,CACT,IAAI,CACF,oBAAoB,EACpB,SAAS,GAAG,eAAe,GAAG,iBAAiB,GAAG,eAAe,CAClE,CACF,GAAG;IAAE,cAAc,EAAE,aAAa,EAAE,CAAA;CAAE,CAQtC;AAED,wBAAgB,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAU/C;AAED,wBAAgB,mBAAmB,IAAI,MAAM,CAgB5C;AAkGD,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,SAAS,GAAG,OAAO,CAAC;AAE1D;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,YAAY,EACnB,UAAU,EAAE,MAAM,EAClB,GAAG,SAAa,GACf;IAAE,MAAM,EAAE,aAAa,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAQ5C;AAuCD,qEAAqE;AACrE,wBAAgB,mBAAmB,CAAC,SAAS,CAAC,EAAE,oBAAoB,GAAG,MAAM,IAAI,CA6BhF;AAED,6DAA6D;AAC7D,wBAAgB,2BAA2B,IAAI,IAAI,CAQlD"}
1
+ {"version":3,"file":"monitor.d.ts","sourceRoot":"","sources":["../src/monitor.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,SAAS,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,cAAc,CAAC,EAAE,aAAa,EAAE,CAAC;IACjC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,SAAS,GAAG,OAAO,CAAC;AAC1D,MAAM,MAAM,4BAA4B,GAAG,UAAU,CAAC,OAAO,2BAA2B,CAAC,CAAC;AAE1F,eAAO,MAAM,mBAAmB,yCAG/B,CAAC;AAEF,wBAAgB,2BAA2B,CAAC,GAAG,EAAE,oBAAoB,GAAG,SAAS;;;;;;EAQhF;AAED,wBAAgB,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAS/C;AAED,wBAAgB,eAAe,CAC7B,KAAK,EAAE,YAAY,EACnB,UAAU,EAAE,MAAM,EAClB,GAAG,SAAa,GACf;IAAE,MAAM,EAAE,aAAa,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAO5C;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;CAC/B;AAED,+FAA+F;AAC/F,qBAAa,cAAc;;gBAUb,MAAM,GAAE,oBAAyB,EAAE,OAAO,GAAE,qBAA0B;IAQlF,IAAI,KAAK,IAAI,QAAQ,CAAC,YAAY,CAAC,CAElC;IAED,IAAI,OAAO,IAAI,OAAO,CAErB;IAED,KAAK,IAAI,IAAI;IAiBb,OAAO,IAAI,IAAI;IASf,YAAY,IAAI,MAAM;CA2EvB"}