@chrysb/alphaclaw 0.3.2-beta.3 → 0.3.2-beta.4
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.
|
@@ -366,7 +366,7 @@ export const WatchdogTab = ({
|
|
|
366
366
|
detail=${`${formatBytes(r.disk?.usedBytes)} / ${formatBytes(r.disk?.totalBytes)}`}
|
|
367
367
|
/>
|
|
368
368
|
<${ResourceBar}
|
|
369
|
-
label=${`CPU${r.cpu?.cores ? ` (${r.cpu.cores}
|
|
369
|
+
label=${`CPU${r.cpu?.cores ? ` (${r.cpu.cores} vCPU)` : ""}`}
|
|
370
370
|
percent=${r.cpu?.percent}
|
|
371
371
|
detail=${r.cpu?.percent != null
|
|
372
372
|
? `${r.cpu.percent}%`
|
|
@@ -11,17 +11,54 @@ const readCgroupFile = (filePath) => {
|
|
|
11
11
|
}
|
|
12
12
|
};
|
|
13
13
|
|
|
14
|
+
const readFirstCgroupFile = (paths) => {
|
|
15
|
+
for (const filePath of paths) {
|
|
16
|
+
const value = readCgroupFile(filePath);
|
|
17
|
+
if (value != null) return value;
|
|
18
|
+
}
|
|
19
|
+
return null;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const countCpuSet = (cpuSet) => {
|
|
23
|
+
if (!cpuSet) return null;
|
|
24
|
+
let count = 0;
|
|
25
|
+
const parts = String(cpuSet)
|
|
26
|
+
.split(",")
|
|
27
|
+
.map((part) => part.trim())
|
|
28
|
+
.filter(Boolean);
|
|
29
|
+
for (const part of parts) {
|
|
30
|
+
const [startRaw, endRaw] = part.split("-");
|
|
31
|
+
const start = Number.parseInt(startRaw, 10);
|
|
32
|
+
const end = endRaw == null ? start : Number.parseInt(endRaw, 10);
|
|
33
|
+
if (Number.isNaN(start) || Number.isNaN(end)) continue;
|
|
34
|
+
count += Math.max(0, end - start + 1);
|
|
35
|
+
}
|
|
36
|
+
return count > 0 ? count : null;
|
|
37
|
+
};
|
|
38
|
+
|
|
14
39
|
const parseCgroupMemory = () => {
|
|
15
|
-
const current =
|
|
16
|
-
|
|
40
|
+
const current = readFirstCgroupFile([
|
|
41
|
+
"/sys/fs/cgroup/memory.current",
|
|
42
|
+
"/sys/fs/cgroup/memory/memory.usage_in_bytes",
|
|
43
|
+
]);
|
|
44
|
+
const max = readFirstCgroupFile([
|
|
45
|
+
"/sys/fs/cgroup/memory.max",
|
|
46
|
+
"/sys/fs/cgroup/memory/memory.limit_in_bytes",
|
|
47
|
+
]);
|
|
17
48
|
if (!current) return null;
|
|
18
49
|
const usedBytes = Number.parseInt(current, 10);
|
|
19
50
|
if (Number.isNaN(usedBytes)) return null;
|
|
20
|
-
const
|
|
51
|
+
const parsedLimit =
|
|
21
52
|
max && max !== "max" ? Number.parseInt(max, 10) : null;
|
|
53
|
+
const limitBytes = Number.isNaN(parsedLimit) ? null : parsedLimit;
|
|
54
|
+
// Cgroup v1 uses huge sentinel values to mean "no limit".
|
|
55
|
+
const unlimited =
|
|
56
|
+
limitBytes == null ||
|
|
57
|
+
limitBytes <= 0 ||
|
|
58
|
+
limitBytes >= 9_000_000_000_000_000_000;
|
|
22
59
|
return {
|
|
23
60
|
usedBytes,
|
|
24
|
-
totalBytes:
|
|
61
|
+
totalBytes: unlimited ? null : limitBytes,
|
|
25
62
|
};
|
|
26
63
|
};
|
|
27
64
|
|
|
@@ -41,6 +78,54 @@ const parseCgroupCpu = () => {
|
|
|
41
78
|
};
|
|
42
79
|
};
|
|
43
80
|
|
|
81
|
+
const parseCgroupCpuV1 = () => {
|
|
82
|
+
const usageNs = readFirstCgroupFile([
|
|
83
|
+
"/sys/fs/cgroup/cpuacct/cpuacct.usage",
|
|
84
|
+
"/sys/fs/cgroup/cpu/cpuacct.usage",
|
|
85
|
+
]);
|
|
86
|
+
if (!usageNs) return null;
|
|
87
|
+
const usageNsParsed = Number.parseInt(usageNs, 10);
|
|
88
|
+
if (Number.isNaN(usageNsParsed)) return null;
|
|
89
|
+
return {
|
|
90
|
+
usageUsec: Math.floor(usageNsParsed / 1000),
|
|
91
|
+
userUsec: null,
|
|
92
|
+
systemUsec: null,
|
|
93
|
+
};
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const getAllocatedCpuCores = () => {
|
|
97
|
+
const cpuMax = readCgroupFile("/sys/fs/cgroup/cpu.max");
|
|
98
|
+
if (cpuMax) {
|
|
99
|
+
const [quotaRaw, periodRaw] = cpuMax.split(/\s+/);
|
|
100
|
+
const quota = Number.parseInt(quotaRaw, 10);
|
|
101
|
+
const period = Number.parseInt(periodRaw, 10);
|
|
102
|
+
if (quotaRaw !== "max" && !Number.isNaN(quota) && !Number.isNaN(period) && period > 0) {
|
|
103
|
+
return quota / period;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const quotaV1 = readFirstCgroupFile([
|
|
108
|
+
"/sys/fs/cgroup/cpu/cpu.cfs_quota_us",
|
|
109
|
+
"/sys/fs/cgroup/cpuacct/cpu.cfs_quota_us",
|
|
110
|
+
]);
|
|
111
|
+
const periodV1 = readFirstCgroupFile([
|
|
112
|
+
"/sys/fs/cgroup/cpu/cpu.cfs_period_us",
|
|
113
|
+
"/sys/fs/cgroup/cpuacct/cpu.cfs_period_us",
|
|
114
|
+
]);
|
|
115
|
+
if (quotaV1 && periodV1) {
|
|
116
|
+
const quota = Number.parseInt(quotaV1, 10);
|
|
117
|
+
const period = Number.parseInt(periodV1, 10);
|
|
118
|
+
if (!Number.isNaN(quota) && !Number.isNaN(period) && quota > 0 && period > 0) {
|
|
119
|
+
return quota / period;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const cpuSet =
|
|
124
|
+
readCgroupFile("/sys/fs/cgroup/cpuset.cpus.effective") ||
|
|
125
|
+
readCgroupFile("/sys/fs/cgroup/cpuset.cpus");
|
|
126
|
+
return countCpuSet(cpuSet);
|
|
127
|
+
};
|
|
128
|
+
|
|
44
129
|
const readProcStatus = (pid) => {
|
|
45
130
|
try {
|
|
46
131
|
const status = fs.readFileSync(`/proc/${pid}/status`, "utf8");
|
|
@@ -98,6 +183,8 @@ let prevCpuSnapshot = null;
|
|
|
98
183
|
let prevCpuSnapshotAt = 0;
|
|
99
184
|
|
|
100
185
|
const getSystemResources = ({ gatewayPid = null } = {}) => {
|
|
186
|
+
const hostCores = os.cpus().length || 1;
|
|
187
|
+
const allocatedCores = getAllocatedCpuCores() || hostCores;
|
|
101
188
|
const cgroupMem = parseCgroupMemory();
|
|
102
189
|
const mem = {
|
|
103
190
|
usedBytes: cgroupMem?.usedBytes ?? process.memoryUsage().rss,
|
|
@@ -106,7 +193,7 @@ const getSystemResources = ({ gatewayPid = null } = {}) => {
|
|
|
106
193
|
|
|
107
194
|
const diskUsage = readDiskUsage();
|
|
108
195
|
|
|
109
|
-
const cgroupCpu = parseCgroupCpu();
|
|
196
|
+
const cgroupCpu = parseCgroupCpu() || parseCgroupCpuV1();
|
|
110
197
|
let cpuPercent = null;
|
|
111
198
|
if (cgroupCpu?.usageUsec != null) {
|
|
112
199
|
const now = Date.now();
|
|
@@ -115,15 +202,15 @@ const getSystemResources = ({ gatewayPid = null } = {}) => {
|
|
|
115
202
|
if (elapsedMs > 0) {
|
|
116
203
|
const usageDeltaUs = cgroupCpu.usageUsec - prevCpuSnapshot.usageUsec;
|
|
117
204
|
const elapsedUs = elapsedMs * 1000;
|
|
118
|
-
|
|
205
|
+
const rawPercent = (usageDeltaUs / elapsedUs) * 100;
|
|
206
|
+
cpuPercent = Math.min(100, Math.max(0, rawPercent / allocatedCores));
|
|
119
207
|
}
|
|
120
208
|
}
|
|
121
209
|
prevCpuSnapshot = cgroupCpu;
|
|
122
210
|
prevCpuSnapshotAt = now;
|
|
123
211
|
} else {
|
|
124
212
|
const load = os.loadavg();
|
|
125
|
-
|
|
126
|
-
cpuPercent = Math.min(100, Math.max(0, (load[0] / cpus) * 100));
|
|
213
|
+
cpuPercent = Math.min(100, Math.max(0, (load[0] / allocatedCores) * 100));
|
|
127
214
|
}
|
|
128
215
|
|
|
129
216
|
const alphaclawRss = process.memoryUsage().rss;
|
|
@@ -148,7 +235,8 @@ const getSystemResources = ({ gatewayPid = null } = {}) => {
|
|
|
148
235
|
},
|
|
149
236
|
cpu: {
|
|
150
237
|
percent: cpuPercent != null ? Math.round(cpuPercent * 10) / 10 : null,
|
|
151
|
-
cores:
|
|
238
|
+
cores: Math.round(allocatedCores * 10) / 10,
|
|
239
|
+
hostCores,
|
|
152
240
|
},
|
|
153
241
|
processes: {
|
|
154
242
|
alphaclaw: { rssBytes: alphaclawRss },
|