@livekit/agents 1.0.48 → 1.0.50

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.
Files changed (53) hide show
  1. package/dist/cpu.cjs +189 -0
  2. package/dist/cpu.cjs.map +1 -0
  3. package/dist/cpu.d.cts +24 -0
  4. package/dist/cpu.d.ts +24 -0
  5. package/dist/cpu.d.ts.map +1 -0
  6. package/dist/cpu.js +152 -0
  7. package/dist/cpu.js.map +1 -0
  8. package/dist/cpu.test.cjs +227 -0
  9. package/dist/cpu.test.cjs.map +1 -0
  10. package/dist/cpu.test.js +204 -0
  11. package/dist/cpu.test.js.map +1 -0
  12. package/dist/inference/llm.cjs.map +1 -1
  13. package/dist/inference/llm.d.cts +1 -1
  14. package/dist/inference/llm.d.ts +1 -1
  15. package/dist/inference/llm.d.ts.map +1 -1
  16. package/dist/inference/llm.js.map +1 -1
  17. package/dist/inference/tts.cjs.map +1 -1
  18. package/dist/inference/tts.d.cts +6 -0
  19. package/dist/inference/tts.d.ts +6 -0
  20. package/dist/inference/tts.d.ts.map +1 -1
  21. package/dist/inference/tts.js.map +1 -1
  22. package/dist/ipc/job_proc_lazy_main.cjs +13 -4
  23. package/dist/ipc/job_proc_lazy_main.cjs.map +1 -1
  24. package/dist/ipc/job_proc_lazy_main.js +13 -4
  25. package/dist/ipc/job_proc_lazy_main.js.map +1 -1
  26. package/dist/version.cjs +1 -1
  27. package/dist/version.js +1 -1
  28. package/dist/voice/agent_activity.cjs +39 -8
  29. package/dist/voice/agent_activity.cjs.map +1 -1
  30. package/dist/voice/agent_activity.d.ts.map +1 -1
  31. package/dist/voice/agent_activity.js +40 -9
  32. package/dist/voice/agent_activity.js.map +1 -1
  33. package/dist/voice/agent_session.cjs +27 -1
  34. package/dist/voice/agent_session.cjs.map +1 -1
  35. package/dist/voice/agent_session.d.cts +6 -0
  36. package/dist/voice/agent_session.d.ts +6 -0
  37. package/dist/voice/agent_session.d.ts.map +1 -1
  38. package/dist/voice/agent_session.js +27 -1
  39. package/dist/voice/agent_session.js.map +1 -1
  40. package/dist/worker.cjs +6 -29
  41. package/dist/worker.cjs.map +1 -1
  42. package/dist/worker.d.ts.map +1 -1
  43. package/dist/worker.js +6 -19
  44. package/dist/worker.js.map +1 -1
  45. package/package.json +1 -1
  46. package/src/cpu.test.ts +239 -0
  47. package/src/cpu.ts +173 -0
  48. package/src/inference/llm.ts +2 -0
  49. package/src/inference/tts.ts +8 -1
  50. package/src/ipc/job_proc_lazy_main.ts +15 -4
  51. package/src/voice/agent_activity.ts +68 -10
  52. package/src/voice/agent_session.ts +33 -2
  53. package/src/worker.ts +34 -50
package/dist/cpu.cjs ADDED
@@ -0,0 +1,189 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var cpu_exports = {};
30
+ __export(cpu_exports, {
31
+ CGroupV1CpuMonitor: () => CGroupV1CpuMonitor,
32
+ CGroupV2CpuMonitor: () => CGroupV2CpuMonitor,
33
+ DefaultCpuMonitor: () => DefaultCpuMonitor,
34
+ getCpuMonitor: () => getCpuMonitor
35
+ });
36
+ module.exports = __toCommonJS(cpu_exports);
37
+ var import_node_fs = require("node:fs");
38
+ var import_node_os = __toESM(require("node:os"), 1);
39
+ function cpuCountFromEnv() {
40
+ const raw = process.env.NUM_CPUS;
41
+ if (raw === void 0) return void 0;
42
+ const parsed = parseFloat(raw);
43
+ if (Number.isNaN(parsed)) {
44
+ console.warn("Failed to parse NUM_CPUS from environment");
45
+ return void 0;
46
+ }
47
+ return parsed;
48
+ }
49
+ class DefaultCpuMonitor {
50
+ cpuCount() {
51
+ return cpuCountFromEnv() ?? (import_node_os.default.cpus().length || 1);
52
+ }
53
+ cpuPercent(intervalMs) {
54
+ return new Promise((resolve) => {
55
+ const cpus1 = import_node_os.default.cpus();
56
+ const timer = setTimeout(() => {
57
+ const cpus2 = import_node_os.default.cpus();
58
+ let idle = 0;
59
+ let total = 0;
60
+ for (let i = 0; i < cpus1.length; i++) {
61
+ const cpu1 = cpus1[i].times;
62
+ const cpu2 = cpus2[i].times;
63
+ idle += cpu2.idle - cpu1.idle;
64
+ const total1 = Object.values(cpu1).reduce((acc, v) => acc + v, 0);
65
+ const total2 = Object.values(cpu2).reduce((acc, v) => acc + v, 0);
66
+ total += total2 - total1;
67
+ }
68
+ resolve(total === 0 ? 0 : Math.max(Math.min(+(1 - idle / total).toFixed(2), 1), 0));
69
+ }, intervalMs);
70
+ timer.unref();
71
+ });
72
+ }
73
+ }
74
+ class CGroupV2CpuMonitor {
75
+ cpuCount() {
76
+ const envCpus = cpuCountFromEnv();
77
+ if (envCpus !== void 0) return envCpus;
78
+ const [quota, period] = this.#readCpuMax();
79
+ if (quota === "max") return import_node_os.default.cpus().length || 1;
80
+ return parseInt(quota) / period;
81
+ }
82
+ cpuPercent(intervalMs) {
83
+ return new Promise((resolve, reject) => {
84
+ const usageStart = this.#readCpuUsage();
85
+ const timer = setTimeout(() => {
86
+ try {
87
+ const usageEnd = this.#readCpuUsage();
88
+ const usageDiffUsec = usageEnd - usageStart;
89
+ const usageSeconds = usageDiffUsec / 1e6;
90
+ const numCpus = this.cpuCount();
91
+ const intervalSeconds = intervalMs / 1e3;
92
+ const percent = usageSeconds / (intervalSeconds * numCpus);
93
+ resolve(Math.max(Math.min(percent, 1), 0));
94
+ } catch (e) {
95
+ reject(e);
96
+ }
97
+ }, intervalMs);
98
+ timer.unref();
99
+ });
100
+ }
101
+ #readCpuMax() {
102
+ try {
103
+ const data = (0, import_node_fs.readFileSync)("/sys/fs/cgroup/cpu.max", "utf-8").trim().split(/\s+/);
104
+ const quota = data[0] ?? "max";
105
+ const period = data[1] ? parseInt(data[1]) : 1e5;
106
+ return [quota, Number.isNaN(period) ? 1e5 : period];
107
+ } catch {
108
+ return ["max", 1e5];
109
+ }
110
+ }
111
+ #readCpuUsage() {
112
+ const content = (0, import_node_fs.readFileSync)("/sys/fs/cgroup/cpu.stat", "utf-8");
113
+ for (const line of content.split("\n")) {
114
+ if (line.startsWith("usage_usec")) {
115
+ return parseInt(line.split(/\s+/)[1]);
116
+ }
117
+ }
118
+ throw new Error("Failed to read CPU usage from /sys/fs/cgroup/cpu.stat");
119
+ }
120
+ }
121
+ class CGroupV1CpuMonitor {
122
+ cpuCount() {
123
+ const envCpus = cpuCountFromEnv();
124
+ if (envCpus !== void 0) return envCpus;
125
+ const [quota, period] = this.#readCfsQuotaAndPeriod();
126
+ if (quota === null || quota < 0 || period === null || period <= 0) {
127
+ return 2;
128
+ }
129
+ return Math.max(quota / period, 1);
130
+ }
131
+ cpuPercent(intervalMs) {
132
+ return new Promise((resolve, reject) => {
133
+ const usageStart = this.#readCpuacctUsage();
134
+ const timer = setTimeout(() => {
135
+ try {
136
+ const usageEnd = this.#readCpuacctUsage();
137
+ const usageDiffNs = usageEnd - usageStart;
138
+ const usageSeconds = usageDiffNs / 1e9;
139
+ const numCpus = this.cpuCount();
140
+ const intervalSeconds = intervalMs / 1e3;
141
+ const percent = usageSeconds / (intervalSeconds * numCpus);
142
+ resolve(Math.max(Math.min(percent, 1), 0));
143
+ } catch (e) {
144
+ reject(e);
145
+ }
146
+ }, intervalMs);
147
+ timer.unref();
148
+ });
149
+ }
150
+ #readCfsQuotaAndPeriod() {
151
+ const quota = readFirstInt("/sys/fs/cgroup/cpu/cpu.cfs_quota_us");
152
+ const period = readFirstInt("/sys/fs/cgroup/cpu/cpu.cfs_period_us");
153
+ return [quota, period];
154
+ }
155
+ #readCpuacctUsage() {
156
+ const value = readFirstInt("/sys/fs/cgroup/cpuacct/cpuacct.usage");
157
+ if (value === null) {
158
+ throw new Error("Failed to read cpuacct.usage for cgroup v1");
159
+ }
160
+ return value;
161
+ }
162
+ }
163
+ function readFirstInt(path) {
164
+ try {
165
+ const val = parseInt((0, import_node_fs.readFileSync)(path, "utf-8").trim());
166
+ return Number.isNaN(val) ? null : val;
167
+ } catch {
168
+ return null;
169
+ }
170
+ }
171
+ function isCGroupV2() {
172
+ return (0, import_node_fs.existsSync)("/sys/fs/cgroup/cpu.stat");
173
+ }
174
+ function isCGroupV1() {
175
+ return (0, import_node_fs.existsSync)("/sys/fs/cgroup/cpuacct/cpuacct.usage");
176
+ }
177
+ function getCpuMonitor() {
178
+ if (isCGroupV2()) return new CGroupV2CpuMonitor();
179
+ if (isCGroupV1()) return new CGroupV1CpuMonitor();
180
+ return new DefaultCpuMonitor();
181
+ }
182
+ // Annotate the CommonJS export names for ESM import in node:
183
+ 0 && (module.exports = {
184
+ CGroupV1CpuMonitor,
185
+ CGroupV2CpuMonitor,
186
+ DefaultCpuMonitor,
187
+ getCpuMonitor
188
+ });
189
+ //# sourceMappingURL=cpu.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cpu.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { existsSync, readFileSync } from 'node:fs';\nimport os from 'node:os';\n\n/** @internal */\nexport interface CpuMonitor {\n cpuCount(): number;\n cpuPercent(intervalMs: number): Promise<number>;\n}\n\nfunction cpuCountFromEnv(): number | undefined {\n const raw = process.env.NUM_CPUS;\n if (raw === undefined) return undefined;\n const parsed = parseFloat(raw);\n if (Number.isNaN(parsed)) {\n console.warn('Failed to parse NUM_CPUS from environment');\n return undefined;\n }\n return parsed;\n}\n\n/** @internal */\nexport class DefaultCpuMonitor implements CpuMonitor {\n cpuCount(): number {\n return cpuCountFromEnv() ?? (os.cpus().length || 1);\n }\n\n cpuPercent(intervalMs: number): Promise<number> {\n return new Promise((resolve) => {\n const cpus1 = os.cpus();\n const timer = setTimeout(() => {\n const cpus2 = os.cpus();\n let idle = 0;\n let total = 0;\n for (let i = 0; i < cpus1.length; i++) {\n const cpu1 = cpus1[i]!.times;\n const cpu2 = cpus2[i]!.times;\n idle += cpu2.idle - cpu1.idle;\n const total1 = Object.values(cpu1).reduce((acc, v) => acc + v, 0);\n const total2 = Object.values(cpu2).reduce((acc, v) => acc + v, 0);\n total += total2 - total1;\n }\n resolve(total === 0 ? 0 : Math.max(Math.min(+(1 - idle / total).toFixed(2), 1), 0));\n }, intervalMs);\n timer.unref();\n });\n }\n}\n\n/** @internal */\nexport class CGroupV2CpuMonitor implements CpuMonitor {\n cpuCount(): number {\n const envCpus = cpuCountFromEnv();\n if (envCpus !== undefined) return envCpus;\n const [quota, period] = this.#readCpuMax();\n if (quota === 'max') return os.cpus().length || 1;\n return parseInt(quota) / period;\n }\n\n cpuPercent(intervalMs: number): Promise<number> {\n return new Promise((resolve, reject) => {\n const usageStart = this.#readCpuUsage();\n const timer = setTimeout(() => {\n try {\n const usageEnd = this.#readCpuUsage();\n const usageDiffUsec = usageEnd - usageStart;\n const usageSeconds = usageDiffUsec / 1_000_000;\n const numCpus = this.cpuCount();\n const intervalSeconds = intervalMs / 1000;\n const percent = usageSeconds / (intervalSeconds * numCpus);\n resolve(Math.max(Math.min(percent, 1), 0));\n } catch (e) {\n reject(e);\n }\n }, intervalMs);\n timer.unref();\n });\n }\n\n #readCpuMax(): [string, number] {\n try {\n const data = readFileSync('/sys/fs/cgroup/cpu.max', 'utf-8').trim().split(/\\s+/);\n const quota = data[0] ?? 'max';\n const period = data[1] ? parseInt(data[1]) : 100_000;\n return [quota, Number.isNaN(period) ? 100_000 : period];\n } catch {\n return ['max', 100_000];\n }\n }\n\n #readCpuUsage(): number {\n const content = readFileSync('/sys/fs/cgroup/cpu.stat', 'utf-8');\n for (const line of content.split('\\n')) {\n if (line.startsWith('usage_usec')) {\n return parseInt(line.split(/\\s+/)[1]!);\n }\n }\n throw new Error('Failed to read CPU usage from /sys/fs/cgroup/cpu.stat');\n }\n}\n\n/** @internal */\nexport class CGroupV1CpuMonitor implements CpuMonitor {\n cpuCount(): number {\n const envCpus = cpuCountFromEnv();\n if (envCpus !== undefined) return envCpus;\n const [quota, period] = this.#readCfsQuotaAndPeriod();\n if (quota === null || quota < 0 || period === null || period <= 0) {\n // do not use the host CPU count as it could overstate the number available to the container\n return 2.0;\n }\n return Math.max(quota / period, 1.0);\n }\n\n cpuPercent(intervalMs: number): Promise<number> {\n return new Promise((resolve, reject) => {\n const usageStart = this.#readCpuacctUsage();\n const timer = setTimeout(() => {\n try {\n const usageEnd = this.#readCpuacctUsage();\n const usageDiffNs = usageEnd - usageStart;\n const usageSeconds = usageDiffNs / 1_000_000_000;\n const numCpus = this.cpuCount();\n const intervalSeconds = intervalMs / 1000;\n const percent = usageSeconds / (intervalSeconds * numCpus);\n resolve(Math.max(Math.min(percent, 1.0), 0.0));\n } catch (e) {\n reject(e);\n }\n }, intervalMs);\n timer.unref();\n });\n }\n\n #readCfsQuotaAndPeriod(): [number | null, number | null] {\n const quota = readFirstInt('/sys/fs/cgroup/cpu/cpu.cfs_quota_us');\n const period = readFirstInt('/sys/fs/cgroup/cpu/cpu.cfs_period_us');\n return [quota, period];\n }\n\n #readCpuacctUsage(): number {\n const value = readFirstInt('/sys/fs/cgroup/cpuacct/cpuacct.usage');\n if (value === null) {\n throw new Error('Failed to read cpuacct.usage for cgroup v1');\n }\n return value;\n }\n}\n\nfunction readFirstInt(path: string): number | null {\n try {\n const val = parseInt(readFileSync(path, 'utf-8').trim());\n return Number.isNaN(val) ? null : val;\n } catch {\n return null;\n }\n}\n\nfunction isCGroupV2(): boolean {\n return existsSync('/sys/fs/cgroup/cpu.stat');\n}\n\nfunction isCGroupV1(): boolean {\n return existsSync('/sys/fs/cgroup/cpuacct/cpuacct.usage');\n}\n\nexport function getCpuMonitor(): CpuMonitor {\n if (isCGroupV2()) return new CGroupV2CpuMonitor();\n if (isCGroupV1()) return new CGroupV1CpuMonitor();\n return new DefaultCpuMonitor();\n}\n"],"mappings":"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","names":["os"]}
package/dist/cpu.d.cts ADDED
@@ -0,0 +1,24 @@
1
+ /** @internal */
2
+ export interface CpuMonitor {
3
+ cpuCount(): number;
4
+ cpuPercent(intervalMs: number): Promise<number>;
5
+ }
6
+ /** @internal */
7
+ export declare class DefaultCpuMonitor implements CpuMonitor {
8
+ cpuCount(): number;
9
+ cpuPercent(intervalMs: number): Promise<number>;
10
+ }
11
+ /** @internal */
12
+ export declare class CGroupV2CpuMonitor implements CpuMonitor {
13
+ #private;
14
+ cpuCount(): number;
15
+ cpuPercent(intervalMs: number): Promise<number>;
16
+ }
17
+ /** @internal */
18
+ export declare class CGroupV1CpuMonitor implements CpuMonitor {
19
+ #private;
20
+ cpuCount(): number;
21
+ cpuPercent(intervalMs: number): Promise<number>;
22
+ }
23
+ export declare function getCpuMonitor(): CpuMonitor;
24
+ //# sourceMappingURL=cpu.d.ts.map
package/dist/cpu.d.ts ADDED
@@ -0,0 +1,24 @@
1
+ /** @internal */
2
+ export interface CpuMonitor {
3
+ cpuCount(): number;
4
+ cpuPercent(intervalMs: number): Promise<number>;
5
+ }
6
+ /** @internal */
7
+ export declare class DefaultCpuMonitor implements CpuMonitor {
8
+ cpuCount(): number;
9
+ cpuPercent(intervalMs: number): Promise<number>;
10
+ }
11
+ /** @internal */
12
+ export declare class CGroupV2CpuMonitor implements CpuMonitor {
13
+ #private;
14
+ cpuCount(): number;
15
+ cpuPercent(intervalMs: number): Promise<number>;
16
+ }
17
+ /** @internal */
18
+ export declare class CGroupV1CpuMonitor implements CpuMonitor {
19
+ #private;
20
+ cpuCount(): number;
21
+ cpuPercent(intervalMs: number): Promise<number>;
22
+ }
23
+ export declare function getCpuMonitor(): CpuMonitor;
24
+ //# sourceMappingURL=cpu.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cpu.d.ts","sourceRoot":"","sources":["../src/cpu.ts"],"names":[],"mappings":"AAMA,gBAAgB;AAChB,MAAM,WAAW,UAAU;IACzB,QAAQ,IAAI,MAAM,CAAC;IACnB,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CACjD;AAaD,gBAAgB;AAChB,qBAAa,iBAAkB,YAAW,UAAU;IAClD,QAAQ,IAAI,MAAM;IAIlB,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAoBhD;AAED,gBAAgB;AAChB,qBAAa,kBAAmB,YAAW,UAAU;;IACnD,QAAQ,IAAI,MAAM;IAQlB,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAwChD;AAED,gBAAgB;AAChB,qBAAa,kBAAmB,YAAW,UAAU;;IACnD,QAAQ,IAAI,MAAM;IAWlB,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAiChD;AAmBD,wBAAgB,aAAa,IAAI,UAAU,CAI1C"}
package/dist/cpu.js ADDED
@@ -0,0 +1,152 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import os from "node:os";
3
+ function cpuCountFromEnv() {
4
+ const raw = process.env.NUM_CPUS;
5
+ if (raw === void 0) return void 0;
6
+ const parsed = parseFloat(raw);
7
+ if (Number.isNaN(parsed)) {
8
+ console.warn("Failed to parse NUM_CPUS from environment");
9
+ return void 0;
10
+ }
11
+ return parsed;
12
+ }
13
+ class DefaultCpuMonitor {
14
+ cpuCount() {
15
+ return cpuCountFromEnv() ?? (os.cpus().length || 1);
16
+ }
17
+ cpuPercent(intervalMs) {
18
+ return new Promise((resolve) => {
19
+ const cpus1 = os.cpus();
20
+ const timer = setTimeout(() => {
21
+ const cpus2 = os.cpus();
22
+ let idle = 0;
23
+ let total = 0;
24
+ for (let i = 0; i < cpus1.length; i++) {
25
+ const cpu1 = cpus1[i].times;
26
+ const cpu2 = cpus2[i].times;
27
+ idle += cpu2.idle - cpu1.idle;
28
+ const total1 = Object.values(cpu1).reduce((acc, v) => acc + v, 0);
29
+ const total2 = Object.values(cpu2).reduce((acc, v) => acc + v, 0);
30
+ total += total2 - total1;
31
+ }
32
+ resolve(total === 0 ? 0 : Math.max(Math.min(+(1 - idle / total).toFixed(2), 1), 0));
33
+ }, intervalMs);
34
+ timer.unref();
35
+ });
36
+ }
37
+ }
38
+ class CGroupV2CpuMonitor {
39
+ cpuCount() {
40
+ const envCpus = cpuCountFromEnv();
41
+ if (envCpus !== void 0) return envCpus;
42
+ const [quota, period] = this.#readCpuMax();
43
+ if (quota === "max") return os.cpus().length || 1;
44
+ return parseInt(quota) / period;
45
+ }
46
+ cpuPercent(intervalMs) {
47
+ return new Promise((resolve, reject) => {
48
+ const usageStart = this.#readCpuUsage();
49
+ const timer = setTimeout(() => {
50
+ try {
51
+ const usageEnd = this.#readCpuUsage();
52
+ const usageDiffUsec = usageEnd - usageStart;
53
+ const usageSeconds = usageDiffUsec / 1e6;
54
+ const numCpus = this.cpuCount();
55
+ const intervalSeconds = intervalMs / 1e3;
56
+ const percent = usageSeconds / (intervalSeconds * numCpus);
57
+ resolve(Math.max(Math.min(percent, 1), 0));
58
+ } catch (e) {
59
+ reject(e);
60
+ }
61
+ }, intervalMs);
62
+ timer.unref();
63
+ });
64
+ }
65
+ #readCpuMax() {
66
+ try {
67
+ const data = readFileSync("/sys/fs/cgroup/cpu.max", "utf-8").trim().split(/\s+/);
68
+ const quota = data[0] ?? "max";
69
+ const period = data[1] ? parseInt(data[1]) : 1e5;
70
+ return [quota, Number.isNaN(period) ? 1e5 : period];
71
+ } catch {
72
+ return ["max", 1e5];
73
+ }
74
+ }
75
+ #readCpuUsage() {
76
+ const content = readFileSync("/sys/fs/cgroup/cpu.stat", "utf-8");
77
+ for (const line of content.split("\n")) {
78
+ if (line.startsWith("usage_usec")) {
79
+ return parseInt(line.split(/\s+/)[1]);
80
+ }
81
+ }
82
+ throw new Error("Failed to read CPU usage from /sys/fs/cgroup/cpu.stat");
83
+ }
84
+ }
85
+ class CGroupV1CpuMonitor {
86
+ cpuCount() {
87
+ const envCpus = cpuCountFromEnv();
88
+ if (envCpus !== void 0) return envCpus;
89
+ const [quota, period] = this.#readCfsQuotaAndPeriod();
90
+ if (quota === null || quota < 0 || period === null || period <= 0) {
91
+ return 2;
92
+ }
93
+ return Math.max(quota / period, 1);
94
+ }
95
+ cpuPercent(intervalMs) {
96
+ return new Promise((resolve, reject) => {
97
+ const usageStart = this.#readCpuacctUsage();
98
+ const timer = setTimeout(() => {
99
+ try {
100
+ const usageEnd = this.#readCpuacctUsage();
101
+ const usageDiffNs = usageEnd - usageStart;
102
+ const usageSeconds = usageDiffNs / 1e9;
103
+ const numCpus = this.cpuCount();
104
+ const intervalSeconds = intervalMs / 1e3;
105
+ const percent = usageSeconds / (intervalSeconds * numCpus);
106
+ resolve(Math.max(Math.min(percent, 1), 0));
107
+ } catch (e) {
108
+ reject(e);
109
+ }
110
+ }, intervalMs);
111
+ timer.unref();
112
+ });
113
+ }
114
+ #readCfsQuotaAndPeriod() {
115
+ const quota = readFirstInt("/sys/fs/cgroup/cpu/cpu.cfs_quota_us");
116
+ const period = readFirstInt("/sys/fs/cgroup/cpu/cpu.cfs_period_us");
117
+ return [quota, period];
118
+ }
119
+ #readCpuacctUsage() {
120
+ const value = readFirstInt("/sys/fs/cgroup/cpuacct/cpuacct.usage");
121
+ if (value === null) {
122
+ throw new Error("Failed to read cpuacct.usage for cgroup v1");
123
+ }
124
+ return value;
125
+ }
126
+ }
127
+ function readFirstInt(path) {
128
+ try {
129
+ const val = parseInt(readFileSync(path, "utf-8").trim());
130
+ return Number.isNaN(val) ? null : val;
131
+ } catch {
132
+ return null;
133
+ }
134
+ }
135
+ function isCGroupV2() {
136
+ return existsSync("/sys/fs/cgroup/cpu.stat");
137
+ }
138
+ function isCGroupV1() {
139
+ return existsSync("/sys/fs/cgroup/cpuacct/cpuacct.usage");
140
+ }
141
+ function getCpuMonitor() {
142
+ if (isCGroupV2()) return new CGroupV2CpuMonitor();
143
+ if (isCGroupV1()) return new CGroupV1CpuMonitor();
144
+ return new DefaultCpuMonitor();
145
+ }
146
+ export {
147
+ CGroupV1CpuMonitor,
148
+ CGroupV2CpuMonitor,
149
+ DefaultCpuMonitor,
150
+ getCpuMonitor
151
+ };
152
+ //# sourceMappingURL=cpu.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cpu.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { existsSync, readFileSync } from 'node:fs';\nimport os from 'node:os';\n\n/** @internal */\nexport interface CpuMonitor {\n cpuCount(): number;\n cpuPercent(intervalMs: number): Promise<number>;\n}\n\nfunction cpuCountFromEnv(): number | undefined {\n const raw = process.env.NUM_CPUS;\n if (raw === undefined) return undefined;\n const parsed = parseFloat(raw);\n if (Number.isNaN(parsed)) {\n console.warn('Failed to parse NUM_CPUS from environment');\n return undefined;\n }\n return parsed;\n}\n\n/** @internal */\nexport class DefaultCpuMonitor implements CpuMonitor {\n cpuCount(): number {\n return cpuCountFromEnv() ?? (os.cpus().length || 1);\n }\n\n cpuPercent(intervalMs: number): Promise<number> {\n return new Promise((resolve) => {\n const cpus1 = os.cpus();\n const timer = setTimeout(() => {\n const cpus2 = os.cpus();\n let idle = 0;\n let total = 0;\n for (let i = 0; i < cpus1.length; i++) {\n const cpu1 = cpus1[i]!.times;\n const cpu2 = cpus2[i]!.times;\n idle += cpu2.idle - cpu1.idle;\n const total1 = Object.values(cpu1).reduce((acc, v) => acc + v, 0);\n const total2 = Object.values(cpu2).reduce((acc, v) => acc + v, 0);\n total += total2 - total1;\n }\n resolve(total === 0 ? 0 : Math.max(Math.min(+(1 - idle / total).toFixed(2), 1), 0));\n }, intervalMs);\n timer.unref();\n });\n }\n}\n\n/** @internal */\nexport class CGroupV2CpuMonitor implements CpuMonitor {\n cpuCount(): number {\n const envCpus = cpuCountFromEnv();\n if (envCpus !== undefined) return envCpus;\n const [quota, period] = this.#readCpuMax();\n if (quota === 'max') return os.cpus().length || 1;\n return parseInt(quota) / period;\n }\n\n cpuPercent(intervalMs: number): Promise<number> {\n return new Promise((resolve, reject) => {\n const usageStart = this.#readCpuUsage();\n const timer = setTimeout(() => {\n try {\n const usageEnd = this.#readCpuUsage();\n const usageDiffUsec = usageEnd - usageStart;\n const usageSeconds = usageDiffUsec / 1_000_000;\n const numCpus = this.cpuCount();\n const intervalSeconds = intervalMs / 1000;\n const percent = usageSeconds / (intervalSeconds * numCpus);\n resolve(Math.max(Math.min(percent, 1), 0));\n } catch (e) {\n reject(e);\n }\n }, intervalMs);\n timer.unref();\n });\n }\n\n #readCpuMax(): [string, number] {\n try {\n const data = readFileSync('/sys/fs/cgroup/cpu.max', 'utf-8').trim().split(/\\s+/);\n const quota = data[0] ?? 'max';\n const period = data[1] ? parseInt(data[1]) : 100_000;\n return [quota, Number.isNaN(period) ? 100_000 : period];\n } catch {\n return ['max', 100_000];\n }\n }\n\n #readCpuUsage(): number {\n const content = readFileSync('/sys/fs/cgroup/cpu.stat', 'utf-8');\n for (const line of content.split('\\n')) {\n if (line.startsWith('usage_usec')) {\n return parseInt(line.split(/\\s+/)[1]!);\n }\n }\n throw new Error('Failed to read CPU usage from /sys/fs/cgroup/cpu.stat');\n }\n}\n\n/** @internal */\nexport class CGroupV1CpuMonitor implements CpuMonitor {\n cpuCount(): number {\n const envCpus = cpuCountFromEnv();\n if (envCpus !== undefined) return envCpus;\n const [quota, period] = this.#readCfsQuotaAndPeriod();\n if (quota === null || quota < 0 || period === null || period <= 0) {\n // do not use the host CPU count as it could overstate the number available to the container\n return 2.0;\n }\n return Math.max(quota / period, 1.0);\n }\n\n cpuPercent(intervalMs: number): Promise<number> {\n return new Promise((resolve, reject) => {\n const usageStart = this.#readCpuacctUsage();\n const timer = setTimeout(() => {\n try {\n const usageEnd = this.#readCpuacctUsage();\n const usageDiffNs = usageEnd - usageStart;\n const usageSeconds = usageDiffNs / 1_000_000_000;\n const numCpus = this.cpuCount();\n const intervalSeconds = intervalMs / 1000;\n const percent = usageSeconds / (intervalSeconds * numCpus);\n resolve(Math.max(Math.min(percent, 1.0), 0.0));\n } catch (e) {\n reject(e);\n }\n }, intervalMs);\n timer.unref();\n });\n }\n\n #readCfsQuotaAndPeriod(): [number | null, number | null] {\n const quota = readFirstInt('/sys/fs/cgroup/cpu/cpu.cfs_quota_us');\n const period = readFirstInt('/sys/fs/cgroup/cpu/cpu.cfs_period_us');\n return [quota, period];\n }\n\n #readCpuacctUsage(): number {\n const value = readFirstInt('/sys/fs/cgroup/cpuacct/cpuacct.usage');\n if (value === null) {\n throw new Error('Failed to read cpuacct.usage for cgroup v1');\n }\n return value;\n }\n}\n\nfunction readFirstInt(path: string): number | null {\n try {\n const val = parseInt(readFileSync(path, 'utf-8').trim());\n return Number.isNaN(val) ? null : val;\n } catch {\n return null;\n }\n}\n\nfunction isCGroupV2(): boolean {\n return existsSync('/sys/fs/cgroup/cpu.stat');\n}\n\nfunction isCGroupV1(): boolean {\n return existsSync('/sys/fs/cgroup/cpuacct/cpuacct.usage');\n}\n\nexport function getCpuMonitor(): CpuMonitor {\n if (isCGroupV2()) return new CGroupV2CpuMonitor();\n if (isCGroupV1()) return new CGroupV1CpuMonitor();\n return new DefaultCpuMonitor();\n}\n"],"mappings":"AAGA,SAAS,YAAY,oBAAoB;AACzC,OAAO,QAAQ;AAQf,SAAS,kBAAsC;AAC7C,QAAM,MAAM,QAAQ,IAAI;AACxB,MAAI,QAAQ,OAAW,QAAO;AAC9B,QAAM,SAAS,WAAW,GAAG;AAC7B,MAAI,OAAO,MAAM,MAAM,GAAG;AACxB,YAAQ,KAAK,2CAA2C;AACxD,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAGO,MAAM,kBAAwC;AAAA,EACnD,WAAmB;AACjB,WAAO,gBAAgB,MAAM,GAAG,KAAK,EAAE,UAAU;AAAA,EACnD;AAAA,EAEA,WAAW,YAAqC;AAC9C,WAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,YAAM,QAAQ,GAAG,KAAK;AACtB,YAAM,QAAQ,WAAW,MAAM;AAC7B,cAAM,QAAQ,GAAG,KAAK;AACtB,YAAI,OAAO;AACX,YAAI,QAAQ;AACZ,iBAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,gBAAM,OAAO,MAAM,CAAC,EAAG;AACvB,gBAAM,OAAO,MAAM,CAAC,EAAG;AACvB,kBAAQ,KAAK,OAAO,KAAK;AACzB,gBAAM,SAAS,OAAO,OAAO,IAAI,EAAE,OAAO,CAAC,KAAK,MAAM,MAAM,GAAG,CAAC;AAChE,gBAAM,SAAS,OAAO,OAAO,IAAI,EAAE,OAAO,CAAC,KAAK,MAAM,MAAM,GAAG,CAAC;AAChE,mBAAS,SAAS;AAAA,QACpB;AACA,gBAAQ,UAAU,IAAI,IAAI,KAAK,IAAI,KAAK,IAAI,EAAE,IAAI,OAAO,OAAO,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AAAA,MACpF,GAAG,UAAU;AACb,YAAM,MAAM;AAAA,IACd,CAAC;AAAA,EACH;AACF;AAGO,MAAM,mBAAyC;AAAA,EACpD,WAAmB;AACjB,UAAM,UAAU,gBAAgB;AAChC,QAAI,YAAY,OAAW,QAAO;AAClC,UAAM,CAAC,OAAO,MAAM,IAAI,KAAK,YAAY;AACzC,QAAI,UAAU,MAAO,QAAO,GAAG,KAAK,EAAE,UAAU;AAChD,WAAO,SAAS,KAAK,IAAI;AAAA,EAC3B;AAAA,EAEA,WAAW,YAAqC;AAC9C,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,YAAM,aAAa,KAAK,cAAc;AACtC,YAAM,QAAQ,WAAW,MAAM;AAC7B,YAAI;AACF,gBAAM,WAAW,KAAK,cAAc;AACpC,gBAAM,gBAAgB,WAAW;AACjC,gBAAM,eAAe,gBAAgB;AACrC,gBAAM,UAAU,KAAK,SAAS;AAC9B,gBAAM,kBAAkB,aAAa;AACrC,gBAAM,UAAU,gBAAgB,kBAAkB;AAClD,kBAAQ,KAAK,IAAI,KAAK,IAAI,SAAS,CAAC,GAAG,CAAC,CAAC;AAAA,QAC3C,SAAS,GAAG;AACV,iBAAO,CAAC;AAAA,QACV;AAAA,MACF,GAAG,UAAU;AACb,YAAM,MAAM;AAAA,IACd,CAAC;AAAA,EACH;AAAA,EAEA,cAAgC;AAC9B,QAAI;AACF,YAAM,OAAO,aAAa,0BAA0B,OAAO,EAAE,KAAK,EAAE,MAAM,KAAK;AAC/E,YAAM,QAAQ,KAAK,CAAC,KAAK;AACzB,YAAM,SAAS,KAAK,CAAC,IAAI,SAAS,KAAK,CAAC,CAAC,IAAI;AAC7C,aAAO,CAAC,OAAO,OAAO,MAAM,MAAM,IAAI,MAAU,MAAM;AAAA,IACxD,QAAQ;AACN,aAAO,CAAC,OAAO,GAAO;AAAA,IACxB;AAAA,EACF;AAAA,EAEA,gBAAwB;AACtB,UAAM,UAAU,aAAa,2BAA2B,OAAO;AAC/D,eAAW,QAAQ,QAAQ,MAAM,IAAI,GAAG;AACtC,UAAI,KAAK,WAAW,YAAY,GAAG;AACjC,eAAO,SAAS,KAAK,MAAM,KAAK,EAAE,CAAC,CAAE;AAAA,MACvC;AAAA,IACF;AACA,UAAM,IAAI,MAAM,uDAAuD;AAAA,EACzE;AACF;AAGO,MAAM,mBAAyC;AAAA,EACpD,WAAmB;AACjB,UAAM,UAAU,gBAAgB;AAChC,QAAI,YAAY,OAAW,QAAO;AAClC,UAAM,CAAC,OAAO,MAAM,IAAI,KAAK,uBAAuB;AACpD,QAAI,UAAU,QAAQ,QAAQ,KAAK,WAAW,QAAQ,UAAU,GAAG;AAEjE,aAAO;AAAA,IACT;AACA,WAAO,KAAK,IAAI,QAAQ,QAAQ,CAAG;AAAA,EACrC;AAAA,EAEA,WAAW,YAAqC;AAC9C,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,YAAM,aAAa,KAAK,kBAAkB;AAC1C,YAAM,QAAQ,WAAW,MAAM;AAC7B,YAAI;AACF,gBAAM,WAAW,KAAK,kBAAkB;AACxC,gBAAM,cAAc,WAAW;AAC/B,gBAAM,eAAe,cAAc;AACnC,gBAAM,UAAU,KAAK,SAAS;AAC9B,gBAAM,kBAAkB,aAAa;AACrC,gBAAM,UAAU,gBAAgB,kBAAkB;AAClD,kBAAQ,KAAK,IAAI,KAAK,IAAI,SAAS,CAAG,GAAG,CAAG,CAAC;AAAA,QAC/C,SAAS,GAAG;AACV,iBAAO,CAAC;AAAA,QACV;AAAA,MACF,GAAG,UAAU;AACb,YAAM,MAAM;AAAA,IACd,CAAC;AAAA,EACH;AAAA,EAEA,yBAAyD;AACvD,UAAM,QAAQ,aAAa,qCAAqC;AAChE,UAAM,SAAS,aAAa,sCAAsC;AAClE,WAAO,CAAC,OAAO,MAAM;AAAA,EACvB;AAAA,EAEA,oBAA4B;AAC1B,UAAM,QAAQ,aAAa,sCAAsC;AACjE,QAAI,UAAU,MAAM;AAClB,YAAM,IAAI,MAAM,4CAA4C;AAAA,IAC9D;AACA,WAAO;AAAA,EACT;AACF;AAEA,SAAS,aAAa,MAA6B;AACjD,MAAI;AACF,UAAM,MAAM,SAAS,aAAa,MAAM,OAAO,EAAE,KAAK,CAAC;AACvD,WAAO,OAAO,MAAM,GAAG,IAAI,OAAO;AAAA,EACpC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,aAAsB;AAC7B,SAAO,WAAW,yBAAyB;AAC7C;AAEA,SAAS,aAAsB;AAC7B,SAAO,WAAW,sCAAsC;AAC1D;AAEO,SAAS,gBAA4B;AAC1C,MAAI,WAAW,EAAG,QAAO,IAAI,mBAAmB;AAChD,MAAI,WAAW,EAAG,QAAO,IAAI,mBAAmB;AAChD,SAAO,IAAI,kBAAkB;AAC/B;","names":[]}
@@ -0,0 +1,227 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") {
10
+ for (let key of __getOwnPropNames(from))
11
+ if (!__hasOwnProp.call(to, key) && key !== except)
12
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
13
+ }
14
+ return to;
15
+ };
16
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
17
+ // If the importer is in node compatibility mode or this is not an ESM
18
+ // file that has been converted to a CommonJS file using a Babel-
19
+ // compatible transform (i.e. "__esModule" has not been set), then set
20
+ // "default" to the CommonJS "module.exports" for node compatibility.
21
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
22
+ mod
23
+ ));
24
+ var import_node_fs = require("node:fs");
25
+ var import_node_os = __toESM(require("node:os"), 1);
26
+ var import_vitest = require("vitest");
27
+ var import_cpu = require("./cpu.cjs");
28
+ import_vitest.vi.mock("node:fs", () => ({
29
+ existsSync: import_vitest.vi.fn(() => false),
30
+ readFileSync: import_vitest.vi.fn(() => "")
31
+ }));
32
+ const mockExistsSync = import_vitest.vi.mocked(import_node_fs.existsSync);
33
+ const mockReadFileSync = import_vitest.vi.mocked(import_node_fs.readFileSync);
34
+ (0, import_vitest.describe)("cpu", () => {
35
+ (0, import_vitest.beforeEach)(() => {
36
+ import_vitest.vi.clearAllMocks();
37
+ delete process.env.NUM_CPUS;
38
+ });
39
+ (0, import_vitest.afterEach)(() => {
40
+ delete process.env.NUM_CPUS;
41
+ });
42
+ (0, import_vitest.describe)("getCpuMonitor", () => {
43
+ (0, import_vitest.it)("returns CGroupV2CpuMonitor when /sys/fs/cgroup/cpu.stat exists", () => {
44
+ mockExistsSync.mockImplementation((p) => p === "/sys/fs/cgroup/cpu.stat");
45
+ const monitor = (0, import_cpu.getCpuMonitor)();
46
+ (0, import_vitest.expect)(monitor).toBeInstanceOf(import_cpu.CGroupV2CpuMonitor);
47
+ });
48
+ (0, import_vitest.it)("returns CGroupV1CpuMonitor when cgroup v1 paths exist", () => {
49
+ mockExistsSync.mockImplementation((p) => p === "/sys/fs/cgroup/cpuacct/cpuacct.usage");
50
+ const monitor = (0, import_cpu.getCpuMonitor)();
51
+ (0, import_vitest.expect)(monitor).toBeInstanceOf(import_cpu.CGroupV1CpuMonitor);
52
+ });
53
+ (0, import_vitest.it)("returns DefaultCpuMonitor when no cgroup paths exist", () => {
54
+ mockExistsSync.mockReturnValue(false);
55
+ const monitor = (0, import_cpu.getCpuMonitor)();
56
+ (0, import_vitest.expect)(monitor).toBeInstanceOf(import_cpu.DefaultCpuMonitor);
57
+ });
58
+ });
59
+ (0, import_vitest.describe)("DefaultCpuMonitor", () => {
60
+ (0, import_vitest.it)("returns os.cpus().length for cpuCount", () => {
61
+ const monitor = new import_cpu.DefaultCpuMonitor();
62
+ (0, import_vitest.expect)(monitor.cpuCount()).toBe(import_node_os.default.cpus().length);
63
+ });
64
+ (0, import_vitest.it)("respects NUM_CPUS env var", () => {
65
+ process.env.NUM_CPUS = "4.5";
66
+ const monitor = new import_cpu.DefaultCpuMonitor();
67
+ (0, import_vitest.expect)(monitor.cpuCount()).toBe(4.5);
68
+ });
69
+ (0, import_vitest.it)("ignores invalid NUM_CPUS", () => {
70
+ process.env.NUM_CPUS = "notanumber";
71
+ const monitor = new import_cpu.DefaultCpuMonitor();
72
+ (0, import_vitest.expect)(monitor.cpuCount()).toBe(import_node_os.default.cpus().length);
73
+ });
74
+ (0, import_vitest.it)("cpuPercent returns value in [0, 1]", async () => {
75
+ const monitor = new import_cpu.DefaultCpuMonitor();
76
+ const result = await monitor.cpuPercent(50);
77
+ (0, import_vitest.expect)(result).toBeGreaterThanOrEqual(0);
78
+ (0, import_vitest.expect)(result).toBeLessThanOrEqual(1);
79
+ }, 1e4);
80
+ });
81
+ (0, import_vitest.describe)("CGroupV2CpuMonitor", () => {
82
+ (0, import_vitest.it)("returns quota/period for cpuCount", () => {
83
+ mockReadFileSync.mockImplementation((p) => {
84
+ if (String(p) === "/sys/fs/cgroup/cpu.max") return "200000 100000";
85
+ return "";
86
+ });
87
+ const monitor = new import_cpu.CGroupV2CpuMonitor();
88
+ (0, import_vitest.expect)(monitor.cpuCount()).toBe(2);
89
+ });
90
+ (0, import_vitest.it)("falls back to os.cpus().length when quota is max", () => {
91
+ mockReadFileSync.mockImplementation((p) => {
92
+ if (String(p) === "/sys/fs/cgroup/cpu.max") return "max 100000";
93
+ return "";
94
+ });
95
+ const monitor = new import_cpu.CGroupV2CpuMonitor();
96
+ (0, import_vitest.expect)(monitor.cpuCount()).toBe(import_node_os.default.cpus().length);
97
+ });
98
+ (0, import_vitest.it)("handles missing cpu.max gracefully", () => {
99
+ mockReadFileSync.mockImplementation(() => {
100
+ throw new Error("ENOENT");
101
+ });
102
+ const monitor = new import_cpu.CGroupV2CpuMonitor();
103
+ (0, import_vitest.expect)(monitor.cpuCount()).toBe(import_node_os.default.cpus().length);
104
+ });
105
+ (0, import_vitest.it)("respects NUM_CPUS env var", () => {
106
+ process.env.NUM_CPUS = "3";
107
+ const monitor = new import_cpu.CGroupV2CpuMonitor();
108
+ (0, import_vitest.expect)(monitor.cpuCount()).toBe(3);
109
+ });
110
+ (0, import_vitest.it)("cpuPercent computes correct value from usage_usec deltas", async () => {
111
+ let callCount = 0;
112
+ mockReadFileSync.mockImplementation((p) => {
113
+ if (String(p) === "/sys/fs/cgroup/cpu.stat") {
114
+ callCount++;
115
+ return callCount <= 1 ? "usage_usec 1000000\nuser_usec 800000\nsystem_usec 200000" : "usage_usec 2000000\nuser_usec 1600000\nsystem_usec 400000";
116
+ }
117
+ if (String(p) === "/sys/fs/cgroup/cpu.max") return "200000 100000";
118
+ return "";
119
+ });
120
+ const monitor = new import_cpu.CGroupV2CpuMonitor();
121
+ const result = await monitor.cpuPercent(100);
122
+ (0, import_vitest.expect)(result).toBe(1);
123
+ }, 1e4);
124
+ (0, import_vitest.it)("cpuPercent returns fractional load", async () => {
125
+ let callCount = 0;
126
+ mockReadFileSync.mockImplementation((p) => {
127
+ if (String(p) === "/sys/fs/cgroup/cpu.stat") {
128
+ callCount++;
129
+ return callCount <= 1 ? "usage_usec 1000000\n" : "usage_usec 1050000\n";
130
+ }
131
+ if (String(p) === "/sys/fs/cgroup/cpu.max") return "200000 100000";
132
+ return "";
133
+ });
134
+ const monitor = new import_cpu.CGroupV2CpuMonitor();
135
+ const result = await monitor.cpuPercent(100);
136
+ (0, import_vitest.expect)(result).toBeCloseTo(0.25, 1);
137
+ }, 1e4);
138
+ (0, import_vitest.it)("throws when usage_usec is missing from cpu.stat", async () => {
139
+ mockReadFileSync.mockImplementation((p) => {
140
+ if (String(p) === "/sys/fs/cgroup/cpu.stat") return "user_usec 800000\nsystem_usec 200000";
141
+ return "";
142
+ });
143
+ const monitor = new import_cpu.CGroupV2CpuMonitor();
144
+ await (0, import_vitest.expect)(() => monitor.cpuPercent(50)).rejects.toThrow("Failed to read CPU usage");
145
+ });
146
+ });
147
+ (0, import_vitest.describe)("CGroupV1CpuMonitor", () => {
148
+ (0, import_vitest.it)("returns quota/period for cpuCount", () => {
149
+ mockReadFileSync.mockImplementation((p) => {
150
+ if (String(p) === "/sys/fs/cgroup/cpu/cpu.cfs_quota_us") return "200000";
151
+ if (String(p) === "/sys/fs/cgroup/cpu/cpu.cfs_period_us") return "100000";
152
+ return "";
153
+ });
154
+ const monitor = new import_cpu.CGroupV1CpuMonitor();
155
+ (0, import_vitest.expect)(monitor.cpuCount()).toBe(2);
156
+ });
157
+ (0, import_vitest.it)("defaults to 2.0 when quota is -1", () => {
158
+ mockReadFileSync.mockImplementation((p) => {
159
+ if (String(p) === "/sys/fs/cgroup/cpu/cpu.cfs_quota_us") return "-1";
160
+ if (String(p) === "/sys/fs/cgroup/cpu/cpu.cfs_period_us") return "100000";
161
+ return "";
162
+ });
163
+ const monitor = new import_cpu.CGroupV1CpuMonitor();
164
+ (0, import_vitest.expect)(monitor.cpuCount()).toBe(2);
165
+ });
166
+ (0, import_vitest.it)("defaults to 2.0 when quota file is unreadable", () => {
167
+ mockReadFileSync.mockImplementation(() => {
168
+ throw new Error("ENOENT");
169
+ });
170
+ const monitor = new import_cpu.CGroupV1CpuMonitor();
171
+ (0, import_vitest.expect)(monitor.cpuCount()).toBe(2);
172
+ });
173
+ (0, import_vitest.it)("clamps cpuCount to minimum 1.0", () => {
174
+ mockReadFileSync.mockImplementation((p) => {
175
+ if (String(p) === "/sys/fs/cgroup/cpu/cpu.cfs_quota_us") return "50000";
176
+ if (String(p) === "/sys/fs/cgroup/cpu/cpu.cfs_period_us") return "100000";
177
+ return "";
178
+ });
179
+ const monitor = new import_cpu.CGroupV1CpuMonitor();
180
+ (0, import_vitest.expect)(monitor.cpuCount()).toBe(1);
181
+ });
182
+ (0, import_vitest.it)("respects NUM_CPUS env var", () => {
183
+ process.env.NUM_CPUS = "8";
184
+ const monitor = new import_cpu.CGroupV1CpuMonitor();
185
+ (0, import_vitest.expect)(monitor.cpuCount()).toBe(8);
186
+ });
187
+ (0, import_vitest.it)("cpuPercent computes correct value from nanosecond deltas", async () => {
188
+ let callCount = 0;
189
+ mockReadFileSync.mockImplementation((p) => {
190
+ if (String(p) === "/sys/fs/cgroup/cpuacct/cpuacct.usage") {
191
+ callCount++;
192
+ return callCount <= 1 ? "1000000000" : "1100000000";
193
+ }
194
+ if (String(p) === "/sys/fs/cgroup/cpu/cpu.cfs_quota_us") return "200000";
195
+ if (String(p) === "/sys/fs/cgroup/cpu/cpu.cfs_period_us") return "100000";
196
+ return "";
197
+ });
198
+ const monitor = new import_cpu.CGroupV1CpuMonitor();
199
+ const result = await monitor.cpuPercent(100);
200
+ (0, import_vitest.expect)(result).toBeCloseTo(0.5, 1);
201
+ }, 1e4);
202
+ (0, import_vitest.it)("clamps cpuPercent output to [0, 1]", async () => {
203
+ let callCount = 0;
204
+ mockReadFileSync.mockImplementation((p) => {
205
+ if (String(p) === "/sys/fs/cgroup/cpuacct/cpuacct.usage") {
206
+ callCount++;
207
+ return callCount <= 1 ? "0" : "10000000000";
208
+ }
209
+ if (String(p) === "/sys/fs/cgroup/cpu/cpu.cfs_quota_us") return "100000";
210
+ if (String(p) === "/sys/fs/cgroup/cpu/cpu.cfs_period_us") return "100000";
211
+ return "";
212
+ });
213
+ const monitor = new import_cpu.CGroupV1CpuMonitor();
214
+ const result = await monitor.cpuPercent(100);
215
+ (0, import_vitest.expect)(result).toBeLessThanOrEqual(1);
216
+ (0, import_vitest.expect)(result).toBeGreaterThanOrEqual(0);
217
+ }, 1e4);
218
+ (0, import_vitest.it)("throws when cpuacct.usage is unreadable", async () => {
219
+ mockReadFileSync.mockImplementation(() => {
220
+ throw new Error("ENOENT");
221
+ });
222
+ const monitor = new import_cpu.CGroupV1CpuMonitor();
223
+ await (0, import_vitest.expect)(() => monitor.cpuPercent(50)).rejects.toThrow("Failed to read cpuacct.usage");
224
+ });
225
+ });
226
+ });
227
+ //# sourceMappingURL=cpu.test.cjs.map