mioku-plugin-help 2.1.0 → 2.1.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mioku-plugin-help",
3
- "version": "2.1.0",
3
+ "version": "2.1.1",
4
4
  "description": "帮助插件,使用截图服务生成美观的帮助图片,并提供 #状态 指令",
5
5
  "main": "index.ts",
6
6
  "type": "module",
@@ -67,6 +67,71 @@ function safeNumber(value: unknown, fallback = 0): number {
67
67
  return Number.isFinite(n) ? n : fallback;
68
68
  }
69
69
 
70
+ /**
71
+ * Snapshot of the system's CPU tick counters at a point in time. The
72
+ * sum covers every core, so a 4-core box that's fully loaded registers
73
+ * `total = 4 * elapsed_in_ticks`. `idle` is the slice spent idle.
74
+ */
75
+ interface CpuTickSample {
76
+ idle: number;
77
+ total: number;
78
+ ts: number;
79
+ }
80
+
81
+ function sampleCpuTicks(): CpuTickSample {
82
+ let idle = 0;
83
+ let total = 0;
84
+ for (const cpu of os.cpus()) {
85
+ const t = cpu.times;
86
+ idle += t.idle;
87
+ total += t.user + t.nice + t.sys + t.idle + t.irq;
88
+ }
89
+ return { idle, total, ts: Date.now() };
90
+ }
91
+
92
+ /**
93
+ * Baseline sample taken at module load. We seed the cache eagerly so the
94
+ * first `#状态` call (whenever it happens) already has a comparison
95
+ * point — without this, the first call after a long idle period would
96
+ * show 0% even if the system is under load.
97
+ */
98
+ let prevCpuTicks: CpuTickSample = sampleCpuTicks();
99
+
100
+ /**
101
+ * If the gap between two samples is larger than this, the previous
102
+ * sample is too stale to produce a meaningful delta (e.g. the user
103
+ * called `#状态` once, then came back an hour later). Discard and
104
+ * start a fresh baseline.
105
+ */
106
+ const CPU_SAMPLE_MAX_GAP_MS = 30_000;
107
+
108
+ /**
109
+ * System-wide average CPU utilization between the previous sample and
110
+ * now. Returns 0 on the first call after a baseline reset, and a value
111
+ * in [0, 100] on subsequent calls.
112
+ *
113
+ * `os.cpus()` already sums ticks across all cores, so the result is
114
+ * the per-core average — no need to divide by `cpuCores` (the old
115
+ * `process.cpuUsage()` math did that because it summed its own usage
116
+ * across cores; same destination, different source).
117
+ */
118
+ function computeSystemCpuPercent(): number {
119
+ const current = sampleCpuTicks();
120
+ const gap = current.ts - prevCpuTicks.ts;
121
+ if (gap > CPU_SAMPLE_MAX_GAP_MS || gap <= 0) {
122
+ prevCpuTicks = current;
123
+ return 0;
124
+ }
125
+ const idleDiff = current.idle - prevCpuTicks.idle;
126
+ const totalDiff = current.total - prevCpuTicks.total;
127
+ prevCpuTicks = current;
128
+ if (totalDiff <= 0) {
129
+ return 0;
130
+ }
131
+ const percent = 100 - (idleDiff / totalDiff) * 100;
132
+ return Math.max(0, Math.min(100, percent));
133
+ }
134
+
70
135
  /** Detect the active JS runtime and its version. */
71
136
  function detectRuntime(): { name: string; version: string } {
72
137
  if (process.versions.bun) {
@@ -315,16 +380,11 @@ async function collectResources(): Promise<ResourceStatus> {
315
380
  speedMhz >= 1000
316
381
  ? `${(speedMhz / 1000).toFixed(1)} GHz`
317
382
  : `${Math.round(speedMhz)} MHz`;
318
- // CPU% requires a 2nd sample; we compute the delta from a previous tick.
319
- // Keep it simple here: estimate from process.cpuUsage() relative to
320
- // wall-clock since start. The webui module re-samples with sleep; for the
321
- // status panel we accept a less precise reading.
322
- const cpuUser = process.cpuUsage();
323
- const cpuTotalUsec = cpuUser.user + cpuUser.system;
324
- const cpuPercent = Math.min(
325
- 100,
326
- ((cpuTotalUsec / 1e6 / Math.max(1, process.uptime())) * 100) / cpuCores,
327
- );
383
+ // System-wide CPU% from a 2-sample delta. `collectSnapshot` has a 2s
384
+ // TTL, so under normal use we'll have a fresh reading each call. The
385
+ // first call after a baseline reset returns 0 (sentinel) — see
386
+ // `computeSystemCpuPercent` for the gap-discard policy.
387
+ const cpuPercent = computeSystemCpuPercent();
328
388
 
329
389
  // systeminformation.mem() gives buffcache and swap fields that os can't.
330
390
  // Wrap in withTimeout so a single slow call doesn't stall the snapshot.