dsh-any-agent 0.3.9 → 0.4.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.
@@ -0,0 +1,72 @@
1
+ /**
2
+ * 子进程 RSS 读取(只用于观测与内存预算,不参与任何协议)。
3
+ *
4
+ * 平台差异:
5
+ * - Windows:`tasklist /FO CSV /NH` 第五列是 "12,345 K"
6
+ * - Unix:`ps -axo pid=,rss=` 第二列是 KB
7
+ *
8
+ * 读取失败一律返回空 Map / null —— 量不到内存不是故障,绝不能因此回收会话。
9
+ * 结果缓存 5 秒,避免 reaper(默认 30 秒一轮)和 dsh_list 反复拉起进程。
10
+ */
11
+
12
+ import { spawnSync } from 'node:child_process';
13
+
14
+ const CACHE_MS = 5000;
15
+ let cache = { at: 0, map: new Map() };
16
+
17
+ function parseTasklist(out) {
18
+ const map = new Map();
19
+ for (const line of out.split('\n')) {
20
+ const cols = line.match(/"[^"]*"/g);
21
+ if (!cols || cols.length < 5) continue;
22
+ const pid = Number(cols[1].slice(1, -1));
23
+ const kb = /([\d,]+)\s*K/i.exec(cols[4]);
24
+ if (Number.isFinite(pid) && kb) map.set(pid, Number(kb[1].replace(/,/g, '')) / 1024);
25
+ }
26
+ return map;
27
+ }
28
+
29
+ function parsePs(out) {
30
+ const map = new Map();
31
+ for (const line of out.split('\n')) {
32
+ const m = /^\s*(\d+)\s+(\d+)\s*$/.exec(line);
33
+ if (m) map.set(Number(m[1]), Number(m[2]) / 1024);
34
+ }
35
+ return map;
36
+ }
37
+
38
+ /** @returns {Map<number, number>} pid → MB */
39
+ export function readRssMap({ force = false } = {}) {
40
+ const now = Date.now();
41
+ if (!force && now - cache.at < CACHE_MS) return cache.map;
42
+ let map = new Map();
43
+ try {
44
+ const r =
45
+ process.platform === 'win32'
46
+ ? spawnSync('tasklist', ['/FO', 'CSV', '/NH'], {
47
+ encoding: 'utf8',
48
+ timeout: 3000,
49
+ windowsHide: true,
50
+ maxBuffer: 8 << 20,
51
+ })
52
+ : spawnSync('ps', ['-axo', 'pid=,rss='], {
53
+ encoding: 'utf8',
54
+ timeout: 3000,
55
+ maxBuffer: 8 << 20,
56
+ });
57
+ if (r.status === 0 && r.stdout) {
58
+ map = process.platform === 'win32' ? parseTasklist(r.stdout) : parsePs(r.stdout);
59
+ }
60
+ } catch {
61
+ // 读不到就当作未知
62
+ }
63
+ cache = { at: now, map };
64
+ return map;
65
+ }
66
+
67
+ /** @returns {number|null} pid 的 RSS(MB,四舍五入);未知返回 null */
68
+ export function rssMb(pid) {
69
+ if (!pid) return null;
70
+ const v = readRssMap().get(pid);
71
+ return v === undefined ? null : Math.round(v);
72
+ }