@glassmkr/crucible 0.10.3 → 0.11.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.
- package/dist/collect/__tests__/c1-c6.test.d.ts +1 -0
- package/dist/collect/__tests__/c1-c6.test.js +160 -0
- package/dist/collect/__tests__/c1-c6.test.js.map +1 -0
- package/dist/collect/__tests__/c7-c10.test.d.ts +1 -0
- package/dist/collect/__tests__/c7-c10.test.js +271 -0
- package/dist/collect/__tests__/c7-c10.test.js.map +1 -0
- package/dist/collect/bonding.d.ts +37 -0
- package/dist/collect/bonding.js +246 -0
- package/dist/collect/bonding.js.map +1 -0
- package/dist/collect/conntrack.d.ts +19 -0
- package/dist/collect/conntrack.js +82 -1
- package/dist/collect/conntrack.js.map +1 -1
- package/dist/collect/edac.d.ts +2 -0
- package/dist/collect/edac.js +104 -0
- package/dist/collect/edac.js.map +1 -0
- package/dist/collect/fd.d.ts +46 -0
- package/dist/collect/fd.js +148 -0
- package/dist/collect/fd.js.map +1 -1
- package/dist/collect/hardware-raid.d.ts +2 -0
- package/dist/collect/hardware-raid.js +152 -0
- package/dist/collect/hardware-raid.js.map +1 -0
- package/dist/collect/psi.d.ts +20 -0
- package/dist/collect/psi.js +90 -0
- package/dist/collect/psi.js.map +1 -0
- package/dist/collect/reboot-evidence.d.ts +2 -0
- package/dist/collect/reboot-evidence.js +109 -0
- package/dist/collect/reboot-evidence.js.map +1 -0
- package/dist/collect/tcp-stats.d.ts +37 -0
- package/dist/collect/tcp-stats.js +153 -0
- package/dist/collect/tcp-stats.js.map +1 -0
- package/dist/collect/vmstat.d.ts +22 -0
- package/dist/collect/vmstat.js +94 -0
- package/dist/collect/vmstat.js.map +1 -0
- package/dist/collect/zfs.js +94 -0
- package/dist/collect/zfs.js.map +1 -1
- package/dist/index.js +49 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/types.d.ts +211 -0
- package/package.json +1 -1
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
// LACP partner state from /proc/net/bonding.
|
|
2
|
+
//
|
|
3
|
+
// Per CC_SPEC_CRUCIBLE_C7_C10_NETWORK_PROCESS_COLLECTION_2026-05-19.md §2.
|
|
4
|
+
//
|
|
5
|
+
// Existing network.ts has bond-master discovery (which interface is
|
|
6
|
+
// slaved to which bond); this collector parses the bond protocol-level
|
|
7
|
+
// state that bond_slave_down's MII check doesn't surface. A bond's
|
|
8
|
+
// MII layer can report up while its LACP partner has timed out.
|
|
9
|
+
//
|
|
10
|
+
// LACP port state bitfield (IEEE 802.3ad clause 43.4.2.2):
|
|
11
|
+
// bit 0 (0x01): LACP Activity
|
|
12
|
+
// bit 1 (0x02): LACP Timeout
|
|
13
|
+
// bit 2 (0x04): Aggregation
|
|
14
|
+
// bit 3 (0x08): Synchronization
|
|
15
|
+
// bit 4 (0x10): Collecting
|
|
16
|
+
// bit 5 (0x20): Distributing
|
|
17
|
+
// bit 6 (0x40): Defaulted
|
|
18
|
+
// bit 7 (0x80): Expired
|
|
19
|
+
//
|
|
20
|
+
// A healthy partner reports Synchronization + Collecting + Distributing
|
|
21
|
+
// (bits 3,4,5 set; aggregation usually also set). Loss of synchronization
|
|
22
|
+
// is the signal this collector surfaces.
|
|
23
|
+
import { readdirSync } from "fs";
|
|
24
|
+
import { readProcFile } from "../lib/parse.js";
|
|
25
|
+
const LACP_SYNCHRONIZATION_BIT = 0x08;
|
|
26
|
+
export function collectBonding() {
|
|
27
|
+
let entries;
|
|
28
|
+
try {
|
|
29
|
+
entries = readdirSync("/proc/net/bonding");
|
|
30
|
+
}
|
|
31
|
+
catch (err) {
|
|
32
|
+
return {
|
|
33
|
+
available: false,
|
|
34
|
+
reason: bondReason(err),
|
|
35
|
+
bonds: [],
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
const bondNames = entries.filter((e) => /^bond/.test(e));
|
|
39
|
+
if (bondNames.length === 0) {
|
|
40
|
+
return { available: false, reason: "no bond interfaces", bonds: [] };
|
|
41
|
+
}
|
|
42
|
+
const bonds = [];
|
|
43
|
+
for (const name of bondNames) {
|
|
44
|
+
const raw = readProcFile(`/proc/net/bonding/${name}`);
|
|
45
|
+
if (!raw)
|
|
46
|
+
continue;
|
|
47
|
+
const parsed = parseBondFile(name, raw);
|
|
48
|
+
if (parsed)
|
|
49
|
+
bonds.push(parsed);
|
|
50
|
+
}
|
|
51
|
+
return { available: true, bonds };
|
|
52
|
+
}
|
|
53
|
+
function bondReason(err) {
|
|
54
|
+
if (err && typeof err === "object" && "code" in err) {
|
|
55
|
+
const code = err.code;
|
|
56
|
+
if (code === "ENOENT")
|
|
57
|
+
return "bonding module not loaded";
|
|
58
|
+
if (typeof code === "string") {
|
|
59
|
+
return `/proc/net/bonding not accessible: ${code}`;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return "/proc/net/bonding not accessible";
|
|
63
|
+
}
|
|
64
|
+
export function parseBondFile(name, raw) {
|
|
65
|
+
const lines = raw.split("\n");
|
|
66
|
+
// Parse the global bond-level block first (the part before any
|
|
67
|
+
// "Slave Interface:" block).
|
|
68
|
+
const slaveSectionStart = lines.findIndex((l) => l.startsWith("Slave Interface:"));
|
|
69
|
+
const globalLines = slaveSectionStart === -1 ? lines : lines.slice(0, slaveSectionStart);
|
|
70
|
+
let mode = "";
|
|
71
|
+
let lacpRate = null;
|
|
72
|
+
for (const line of globalLines) {
|
|
73
|
+
if (line.startsWith("Bonding Mode:")) {
|
|
74
|
+
mode = line.slice("Bonding Mode:".length).trim();
|
|
75
|
+
}
|
|
76
|
+
else if (line.startsWith("LACP rate:")) {
|
|
77
|
+
lacpRate = line.slice("LACP rate:".length).trim();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
const isLacp = mode.includes("802.3ad");
|
|
81
|
+
// Optional Active Aggregator block (LACP only).
|
|
82
|
+
const activeAggregator = isLacp ? parseActiveAggregator(globalLines) : null;
|
|
83
|
+
// Per-slave blocks.
|
|
84
|
+
const slaves = [];
|
|
85
|
+
if (slaveSectionStart !== -1) {
|
|
86
|
+
let block = [];
|
|
87
|
+
for (let i = slaveSectionStart; i < lines.length; i++) {
|
|
88
|
+
const line = lines[i];
|
|
89
|
+
if (line.startsWith("Slave Interface:")) {
|
|
90
|
+
if (block.length > 0) {
|
|
91
|
+
const slave = parseSlaveBlock(block);
|
|
92
|
+
if (slave)
|
|
93
|
+
slaves.push(slave);
|
|
94
|
+
}
|
|
95
|
+
block = [line];
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
block.push(line);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (block.length > 0) {
|
|
102
|
+
const slave = parseSlaveBlock(block);
|
|
103
|
+
if (slave)
|
|
104
|
+
slaves.push(slave);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
name,
|
|
109
|
+
mode,
|
|
110
|
+
is_lacp: isLacp,
|
|
111
|
+
lacp_rate: lacpRate,
|
|
112
|
+
slaves,
|
|
113
|
+
configured_port_count: slaves.length,
|
|
114
|
+
active_aggregator: activeAggregator,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
function parseActiveAggregator(lines) {
|
|
118
|
+
const start = lines.findIndex((l) => l.trim().startsWith("Active Aggregator Info"));
|
|
119
|
+
if (start === -1)
|
|
120
|
+
return null;
|
|
121
|
+
// Indented key:value lines follow until a blank line or a non-indented
|
|
122
|
+
// line (typically the next section header).
|
|
123
|
+
let id = null;
|
|
124
|
+
let numberOfPorts = null;
|
|
125
|
+
let actorKey = null;
|
|
126
|
+
let partnerKey = null;
|
|
127
|
+
let partnerMac = null;
|
|
128
|
+
for (let i = start + 1; i < lines.length; i++) {
|
|
129
|
+
const line = lines[i];
|
|
130
|
+
if (line === "" || /^\S/.test(line)) {
|
|
131
|
+
// End of indented block (blank line or new top-level section).
|
|
132
|
+
if (line.startsWith("Slave Interface"))
|
|
133
|
+
break;
|
|
134
|
+
if (line === "") {
|
|
135
|
+
// Indented block can include blank lines in some kernel versions;
|
|
136
|
+
// tolerate one blank but bail if the next line is non-indented.
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
141
|
+
const m = line.match(/^\s*([^:]+):\s*(.+)$/);
|
|
142
|
+
if (!m)
|
|
143
|
+
continue;
|
|
144
|
+
const key = m[1].trim();
|
|
145
|
+
const val = m[2].trim();
|
|
146
|
+
if (key === "Aggregator ID")
|
|
147
|
+
id = parseIntSafe(val);
|
|
148
|
+
else if (key === "Number of ports")
|
|
149
|
+
numberOfPorts = parseIntSafe(val);
|
|
150
|
+
else if (key === "Actor Key")
|
|
151
|
+
actorKey = parseIntSafe(val);
|
|
152
|
+
else if (key === "Partner Key")
|
|
153
|
+
partnerKey = parseIntSafe(val);
|
|
154
|
+
else if (key === "Partner Mac Address")
|
|
155
|
+
partnerMac = val;
|
|
156
|
+
}
|
|
157
|
+
if (id === null || numberOfPorts === null)
|
|
158
|
+
return null;
|
|
159
|
+
return {
|
|
160
|
+
id,
|
|
161
|
+
number_of_ports: numberOfPorts,
|
|
162
|
+
actor_key: actorKey,
|
|
163
|
+
partner_key: partnerKey,
|
|
164
|
+
partner_mac_address: partnerMac,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
function parseSlaveBlock(lines) {
|
|
168
|
+
let name = "";
|
|
169
|
+
let miiStatus = "";
|
|
170
|
+
let linkFailureCount = 0;
|
|
171
|
+
let hwAddr = "";
|
|
172
|
+
let aggregatorId = null;
|
|
173
|
+
let partnerChurn = null;
|
|
174
|
+
let partnerPortState = null;
|
|
175
|
+
let inPartnerPdu = false;
|
|
176
|
+
for (const line of lines) {
|
|
177
|
+
if (line.startsWith("Slave Interface:")) {
|
|
178
|
+
name = line.slice("Slave Interface:".length).trim();
|
|
179
|
+
inPartnerPdu = false;
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
if (line.startsWith("MII Status:")) {
|
|
183
|
+
miiStatus = line.slice("MII Status:".length).trim();
|
|
184
|
+
inPartnerPdu = false;
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
if (line.startsWith("Link Failure Count:")) {
|
|
188
|
+
linkFailureCount = parseIntSafe(line.slice("Link Failure Count:".length).trim()) ?? 0;
|
|
189
|
+
inPartnerPdu = false;
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
if (line.startsWith("Permanent HW addr:")) {
|
|
193
|
+
hwAddr = line.slice("Permanent HW addr:".length).trim();
|
|
194
|
+
inPartnerPdu = false;
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
if (line.startsWith("Aggregator ID:")) {
|
|
198
|
+
aggregatorId = parseIntSafe(line.slice("Aggregator ID:".length).trim());
|
|
199
|
+
inPartnerPdu = false;
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
if (line.startsWith("Partner Churn State:")) {
|
|
203
|
+
partnerChurn = line.slice("Partner Churn State:".length).trim();
|
|
204
|
+
inPartnerPdu = false;
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
if (/^\s*details partner lacp pdu:/.test(line)) {
|
|
208
|
+
inPartnerPdu = true;
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
if (/^\s*details actor lacp pdu:/.test(line)) {
|
|
212
|
+
inPartnerPdu = false;
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
if (inPartnerPdu) {
|
|
216
|
+
const m = line.match(/^\s*port state:\s*(\d+)/);
|
|
217
|
+
if (m) {
|
|
218
|
+
partnerPortState = parseIntSafe(m[1]);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
if (!name)
|
|
223
|
+
return null;
|
|
224
|
+
const synchronized = partnerPortState !== null
|
|
225
|
+
? (partnerPortState & LACP_SYNCHRONIZATION_BIT) !== 0
|
|
226
|
+
: null;
|
|
227
|
+
return {
|
|
228
|
+
name,
|
|
229
|
+
mii_status: miiStatus,
|
|
230
|
+
link_failure_count: linkFailureCount,
|
|
231
|
+
permanent_hw_addr: hwAddr,
|
|
232
|
+
aggregator_id: aggregatorId,
|
|
233
|
+
partner_churn_state: partnerChurn,
|
|
234
|
+
partner_lacp_port_state: partnerPortState,
|
|
235
|
+
partner_lacp_synchronized: synchronized,
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
function parseIntSafe(v) {
|
|
239
|
+
const n = Number.parseInt(v, 10);
|
|
240
|
+
return Number.isFinite(n) ? n : null;
|
|
241
|
+
}
|
|
242
|
+
export const __test_only = {
|
|
243
|
+
parseBondFile,
|
|
244
|
+
LACP_SYNCHRONIZATION_BIT,
|
|
245
|
+
};
|
|
246
|
+
//# sourceMappingURL=bonding.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bonding.js","sourceRoot":"","sources":["../../src/collect/bonding.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,EAAE;AACF,2EAA2E;AAC3E,EAAE;AACF,oEAAoE;AACpE,uEAAuE;AACvE,mEAAmE;AACnE,gEAAgE;AAChE,EAAE;AACF,2DAA2D;AAC3D,gCAAgC;AAChC,+BAA+B;AAC/B,8BAA8B;AAC9B,kCAAkC;AAClC,6BAA6B;AAC7B,+BAA+B;AAC/B,4BAA4B;AAC5B,0BAA0B;AAC1B,EAAE;AACF,wEAAwE;AACxE,0EAA0E;AAC1E,yCAAyC;AAEzC,OAAO,EAAE,WAAW,EAAE,MAAM,IAAI,CAAC;AAEjC,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAqC/C,MAAM,wBAAwB,GAAG,IAAI,CAAC;AAEtC,MAAM,UAAU,cAAc;IAC5B,IAAI,OAAiB,CAAC;IACtB,IAAI,CAAC;QACH,OAAO,GAAG,WAAW,CAAC,mBAAmB,CAAC,CAAC;IAC7C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO;YACL,SAAS,EAAE,KAAK;YAChB,MAAM,EAAE,UAAU,CAAC,GAAG,CAAC;YACvB,KAAK,EAAE,EAAE;SACV,CAAC;IACJ,CAAC;IAED,MAAM,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IACzD,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3B,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,oBAAoB,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;IACvE,CAAC;IAED,MAAM,KAAK,GAAW,EAAE,CAAC;IACzB,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;QAC7B,MAAM,GAAG,GAAG,YAAY,CAAC,qBAAqB,IAAI,EAAE,CAAC,CAAC;QACtD,IAAI,CAAC,GAAG;YAAE,SAAS;QACnB,MAAM,MAAM,GAAG,aAAa,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QACxC,IAAI,MAAM;YAAE,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACjC,CAAC;IACD,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;AACpC,CAAC;AAED,SAAS,UAAU,CAAC,GAAY;IAC9B,IAAI,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,MAAM,IAAI,GAAG,EAAE,CAAC;QACpD,MAAM,IAAI,GAAI,GAAyB,CAAC,IAAI,CAAC;QAC7C,IAAI,IAAI,KAAK,QAAQ;YAAE,OAAO,2BAA2B,CAAC;QAC1D,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC7B,OAAO,qCAAqC,IAAI,EAAE,CAAC;QACrD,CAAC;IACH,CAAC;IACD,OAAO,kCAAkC,CAAC;AAC5C,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,IAAY,EAAE,GAAW;IACrD,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAE9B,+DAA+D;IAC/D,6BAA6B;IAC7B,MAAM,iBAAiB,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAC9C,CAAC,CAAC,UAAU,CAAC,kBAAkB,CAAC,CACjC,CAAC;IACF,MAAM,WAAW,GAAG,iBAAiB,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,iBAAiB,CAAC,CAAC;IAEzF,IAAI,IAAI,GAAG,EAAE,CAAC;IACd,IAAI,QAAQ,GAAkB,IAAI,CAAC;IACnC,KAAK,MAAM,IAAI,IAAI,WAAW,EAAE,CAAC;QAC/B,IAAI,IAAI,CAAC,UAAU,CAAC,eAAe,CAAC,EAAE,CAAC;YACrC,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;QACnD,CAAC;aAAM,IAAI,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;YACzC,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;QACpD,CAAC;IACH,CAAC;IACD,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;IAExC,gDAAgD;IAChD,MAAM,gBAAgB,GAAG,MAAM,CAAC,CAAC,CAAC,qBAAqB,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAE5E,oBAAoB;IACpB,MAAM,MAAM,GAAgB,EAAE,CAAC;IAC/B,IAAI,iBAAiB,KAAK,CAAC,CAAC,EAAE,CAAC;QAC7B,IAAI,KAAK,GAAa,EAAE,CAAC;QACzB,KAAK,IAAI,CAAC,GAAG,iBAAiB,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACtD,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YACtB,IAAI,IAAI,CAAC,UAAU,CAAC,kBAAkB,CAAC,EAAE,CAAC;gBACxC,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBACrB,MAAM,KAAK,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC;oBACrC,IAAI,KAAK;wBAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBAChC,CAAC;gBACD,KAAK,GAAG,CAAC,IAAI,CAAC,CAAC;YACjB,CAAC;iBAAM,CAAC;gBACN,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACnB,CAAC;QACH,CAAC;QACD,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACrB,MAAM,KAAK,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC;YACrC,IAAI,KAAK;gBAAE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAChC,CAAC;IACH,CAAC;IAED,OAAO;QACL,IAAI;QACJ,IAAI;QACJ,OAAO,EAAE,MAAM;QACf,SAAS,EAAE,QAAQ;QACnB,MAAM;QACN,qBAAqB,EAAE,MAAM,CAAC,MAAM;QACpC,iBAAiB,EAAE,gBAAgB;KACpC,CAAC;AACJ,CAAC;AAED,SAAS,qBAAqB,CAAC,KAAe;IAC5C,MAAM,KAAK,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAClC,CAAC,CAAC,IAAI,EAAE,CAAC,UAAU,CAAC,wBAAwB,CAAC,CAC9C,CAAC;IACF,IAAI,KAAK,KAAK,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IAE9B,uEAAuE;IACvE,4CAA4C;IAC5C,IAAI,EAAE,GAAkB,IAAI,CAAC;IAC7B,IAAI,aAAa,GAAkB,IAAI,CAAC;IACxC,IAAI,QAAQ,GAAkB,IAAI,CAAC;IACnC,IAAI,UAAU,GAAkB,IAAI,CAAC;IACrC,IAAI,UAAU,GAAkB,IAAI,CAAC;IAErC,KAAK,IAAI,CAAC,GAAG,KAAK,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAC9C,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACtB,IAAI,IAAI,KAAK,EAAE,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACpC,+DAA+D;YAC/D,IAAI,IAAI,CAAC,UAAU,CAAC,iBAAiB,CAAC;gBAAE,MAAM;YAC9C,IAAI,IAAI,KAAK,EAAE,EAAE,CAAC;gBAChB,kEAAkE;gBAClE,gEAAgE;gBAChE,SAAS;YACX,CAAC;YACD,MAAM;QACR,CAAC;QACD,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC;QAC7C,IAAI,CAAC,CAAC;YAAE,SAAS;QACjB,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACxB,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACxB,IAAI,GAAG,KAAK,eAAe;YAAE,EAAE,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;aAC/C,IAAI,GAAG,KAAK,iBAAiB;YAAE,aAAa,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;aACjE,IAAI,GAAG,KAAK,WAAW;YAAE,QAAQ,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;aACtD,IAAI,GAAG,KAAK,aAAa;YAAE,UAAU,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC;aAC1D,IAAI,GAAG,KAAK,qBAAqB;YAAE,UAAU,GAAG,GAAG,CAAC;IAC3D,CAAC;IAED,IAAI,EAAE,KAAK,IAAI,IAAI,aAAa,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IACvD,OAAO;QACL,EAAE;QACF,eAAe,EAAE,aAAa;QAC9B,SAAS,EAAE,QAAQ;QACnB,WAAW,EAAE,UAAU;QACvB,mBAAmB,EAAE,UAAU;KAChC,CAAC;AACJ,CAAC;AAED,SAAS,eAAe,CAAC,KAAe;IACtC,IAAI,IAAI,GAAG,EAAE,CAAC;IACd,IAAI,SAAS,GAAG,EAAE,CAAC;IACnB,IAAI,gBAAgB,GAAG,CAAC,CAAC;IACzB,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,IAAI,YAAY,GAAkB,IAAI,CAAC;IACvC,IAAI,YAAY,GAAkB,IAAI,CAAC;IACvC,IAAI,gBAAgB,GAAkB,IAAI,CAAC;IAE3C,IAAI,YAAY,GAAG,KAAK,CAAC;IACzB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,IAAI,CAAC,UAAU,CAAC,kBAAkB,CAAC,EAAE,CAAC;YACxC,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;YACpD,YAAY,GAAG,KAAK,CAAC;YACrB,SAAS;QACX,CAAC;QACD,IAAI,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;YACnC,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;YACpD,YAAY,GAAG,KAAK,CAAC;YACrB,SAAS;QACX,CAAC;QACD,IAAI,IAAI,CAAC,UAAU,CAAC,qBAAqB,CAAC,EAAE,CAAC;YAC3C,gBAAgB,GAAG,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,qBAAqB,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,CAAC;YACtF,YAAY,GAAG,KAAK,CAAC;YACrB,SAAS;QACX,CAAC;QACD,IAAI,IAAI,CAAC,UAAU,CAAC,oBAAoB,CAAC,EAAE,CAAC;YAC1C,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,oBAAoB,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;YACxD,YAAY,GAAG,KAAK,CAAC;YACrB,SAAS;QACX,CAAC;QACD,IAAI,IAAI,CAAC,UAAU,CAAC,gBAAgB,CAAC,EAAE,CAAC;YACtC,YAAY,GAAG,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;YACxE,YAAY,GAAG,KAAK,CAAC;YACrB,SAAS;QACX,CAAC;QACD,IAAI,IAAI,CAAC,UAAU,CAAC,sBAAsB,CAAC,EAAE,CAAC;YAC5C,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,sBAAsB,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;YAChE,YAAY,GAAG,KAAK,CAAC;YACrB,SAAS;QACX,CAAC;QACD,IAAI,+BAA+B,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAC/C,YAAY,GAAG,IAAI,CAAC;YACpB,SAAS;QACX,CAAC;QACD,IAAI,6BAA6B,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAC7C,YAAY,GAAG,KAAK,CAAC;YACrB,SAAS;QACX,CAAC;QACD,IAAI,YAAY,EAAE,CAAC;YACjB,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,yBAAyB,CAAC,CAAC;YAChD,IAAI,CAAC,EAAE,CAAC;gBACN,gBAAgB,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACxC,CAAC;QACH,CAAC;IACH,CAAC;IAED,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IAEvB,MAAM,YAAY,GAChB,gBAAgB,KAAK,IAAI;QACvB,CAAC,CAAC,CAAC,gBAAgB,GAAG,wBAAwB,CAAC,KAAK,CAAC;QACrD,CAAC,CAAC,IAAI,CAAC;IAEX,OAAO;QACL,IAAI;QACJ,UAAU,EAAE,SAAS;QACrB,kBAAkB,EAAE,gBAAgB;QACpC,iBAAiB,EAAE,MAAM;QACzB,aAAa,EAAE,YAAY;QAC3B,mBAAmB,EAAE,YAAY;QACjC,uBAAuB,EAAE,gBAAgB;QACzC,yBAAyB,EAAE,YAAY;KACxC,CAAC;AACJ,CAAC;AAED,SAAS,YAAY,CAAC,CAAS;IAC7B,MAAM,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACjC,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACvC,CAAC;AAED,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,aAAa;IACb,wBAAwB;CACzB,CAAC"}
|
|
@@ -3,5 +3,24 @@ export interface ConntrackData {
|
|
|
3
3
|
count: number;
|
|
4
4
|
max: number;
|
|
5
5
|
percent: number;
|
|
6
|
+
insert_failed_total?: number;
|
|
7
|
+
drop_total?: number;
|
|
8
|
+
insert_failed_rate_per_sec?: number | null;
|
|
9
|
+
drop_rate_per_sec?: number | null;
|
|
6
10
|
}
|
|
7
11
|
export declare function collectConntrack(): ConntrackData;
|
|
12
|
+
/**
|
|
13
|
+
* Parse /proc/net/stat/nf_conntrack. Format: tab/space-separated header
|
|
14
|
+
* + one row per CPU with hex values. Returns sums across all CPUs for
|
|
15
|
+
* insert_failed and drop columns. Null on read failure or unexpected
|
|
16
|
+
* format.
|
|
17
|
+
*/
|
|
18
|
+
declare function parseConntrackStat(): {
|
|
19
|
+
insert_failed: number;
|
|
20
|
+
drop: number;
|
|
21
|
+
} | null;
|
|
22
|
+
export declare const __test_only: {
|
|
23
|
+
parseConntrackStat: typeof parseConntrackStat;
|
|
24
|
+
resetForTests: () => void;
|
|
25
|
+
};
|
|
26
|
+
export {};
|
|
@@ -1,4 +1,13 @@
|
|
|
1
|
+
// Conntrack collection.
|
|
2
|
+
//
|
|
3
|
+
// Pre-C9: count + max + percent from /proc/sys/net/netfilter/nf_conntrack_*.
|
|
4
|
+
// C9 (2026-05-19): adds insert_failed_total + drop_total cumulative counters
|
|
5
|
+
// from /proc/net/stat/nf_conntrack plus agent-computed per-second rates,
|
|
6
|
+
// following the vmstat (C3) rate-calculation pattern.
|
|
7
|
+
//
|
|
8
|
+
// Per CC_SPEC_CRUCIBLE_C7_C10_NETWORK_PROCESS_COLLECTION_2026-05-19.md §3.
|
|
1
9
|
import { readProcFile } from "../lib/parse.js";
|
|
10
|
+
let previous = null;
|
|
2
11
|
export function collectConntrack() {
|
|
3
12
|
const countRaw = readProcFile("/proc/sys/net/netfilter/nf_conntrack_count");
|
|
4
13
|
const maxRaw = readProcFile("/proc/sys/net/netfilter/nf_conntrack_max");
|
|
@@ -11,6 +20,78 @@ export function collectConntrack() {
|
|
|
11
20
|
return { available: false, count: 0, max: 0, percent: 0 };
|
|
12
21
|
}
|
|
13
22
|
const percent = Math.round(((count / max) * 100) * 10) / 10;
|
|
14
|
-
|
|
23
|
+
// Try the C9 enrichment. If /proc/net/stat/nf_conntrack isn't readable
|
|
24
|
+
// we still return the original ConntrackData shape (older kernels
|
|
25
|
+
// expose count/max without per-CPU stats).
|
|
26
|
+
const stat = parseConntrackStat();
|
|
27
|
+
if (!stat) {
|
|
28
|
+
return { available: true, count, max, percent };
|
|
29
|
+
}
|
|
30
|
+
const nowMs = Date.now();
|
|
31
|
+
let insertRate = null;
|
|
32
|
+
let dropRate = null;
|
|
33
|
+
if (previous) {
|
|
34
|
+
const elapsedSec = (nowMs - previous.capturedAtMs) / 1000;
|
|
35
|
+
if (elapsedSec > 0) {
|
|
36
|
+
const insertDelta = stat.insert_failed - previous.insert_failed;
|
|
37
|
+
const dropDelta = stat.drop - previous.drop;
|
|
38
|
+
// Negative delta = counter reset (host reboot, container restart,
|
|
39
|
+
// or rollover). Treat as a fresh baseline; rates null this tick.
|
|
40
|
+
if (insertDelta >= 0)
|
|
41
|
+
insertRate = insertDelta / elapsedSec;
|
|
42
|
+
if (dropDelta >= 0)
|
|
43
|
+
dropRate = dropDelta / elapsedSec;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
previous = { ...stat, capturedAtMs: nowMs };
|
|
47
|
+
return {
|
|
48
|
+
available: true,
|
|
49
|
+
count,
|
|
50
|
+
max,
|
|
51
|
+
percent,
|
|
52
|
+
insert_failed_total: stat.insert_failed,
|
|
53
|
+
drop_total: stat.drop,
|
|
54
|
+
insert_failed_rate_per_sec: insertRate,
|
|
55
|
+
drop_rate_per_sec: dropRate,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Parse /proc/net/stat/nf_conntrack. Format: tab/space-separated header
|
|
60
|
+
* + one row per CPU with hex values. Returns sums across all CPUs for
|
|
61
|
+
* insert_failed and drop columns. Null on read failure or unexpected
|
|
62
|
+
* format.
|
|
63
|
+
*/
|
|
64
|
+
function parseConntrackStat() {
|
|
65
|
+
const raw = readProcFile("/proc/net/stat/nf_conntrack");
|
|
66
|
+
if (!raw)
|
|
67
|
+
return null;
|
|
68
|
+
const lines = raw.split("\n").filter((l) => l.trim().length > 0);
|
|
69
|
+
if (lines.length < 2)
|
|
70
|
+
return null;
|
|
71
|
+
const header = lines[0].trim().split(/\s+/);
|
|
72
|
+
const insertFailedIdx = header.indexOf("insert_failed");
|
|
73
|
+
const dropIdx = header.indexOf("drop");
|
|
74
|
+
if (insertFailedIdx === -1 || dropIdx === -1)
|
|
75
|
+
return null;
|
|
76
|
+
let insertFailedTotal = 0;
|
|
77
|
+
let dropTotal = 0;
|
|
78
|
+
for (let i = 1; i < lines.length; i++) {
|
|
79
|
+
const parts = lines[i].trim().split(/\s+/);
|
|
80
|
+
if (parts.length <= Math.max(insertFailedIdx, dropIdx))
|
|
81
|
+
continue;
|
|
82
|
+
const insertFailed = parseInt(parts[insertFailedIdx], 16);
|
|
83
|
+
const drop = parseInt(parts[dropIdx], 16);
|
|
84
|
+
if (Number.isFinite(insertFailed))
|
|
85
|
+
insertFailedTotal += insertFailed;
|
|
86
|
+
if (Number.isFinite(drop))
|
|
87
|
+
dropTotal += drop;
|
|
88
|
+
}
|
|
89
|
+
return { insert_failed: insertFailedTotal, drop: dropTotal };
|
|
15
90
|
}
|
|
91
|
+
export const __test_only = {
|
|
92
|
+
parseConntrackStat,
|
|
93
|
+
resetForTests: () => {
|
|
94
|
+
previous = null;
|
|
95
|
+
},
|
|
96
|
+
};
|
|
16
97
|
//# sourceMappingURL=conntrack.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conntrack.js","sourceRoot":"","sources":["../../src/collect/conntrack.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"conntrack.js","sourceRoot":"","sources":["../../src/collect/conntrack.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
// EDAC (Error Detection and Correction) counter collection.
|
|
2
|
+
//
|
|
3
|
+
// Per CC_SPEC_FORGE_FOLLOWUP_C1_C6_ACTIVATION_2026-05-19.md (C1).
|
|
4
|
+
//
|
|
5
|
+
// Kernel surface: /sys/devices/system/edac/mc/mcN/{ce_count,ue_count}
|
|
6
|
+
// ce_count = correctable errors (memory controller saw and fixed)
|
|
7
|
+
// ue_count = uncorrectable errors (memory error couldn't be fixed;
|
|
8
|
+
// kernel may have killed the process or panicked)
|
|
9
|
+
//
|
|
10
|
+
// Per-DIMM granularity: /sys/devices/system/edac/mc/mcN/dimmM/
|
|
11
|
+
// dimm_ce_count, dimm_ue_count, dimm_label, dimm_location, size
|
|
12
|
+
//
|
|
13
|
+
// Capability gate: if no `mcN` directories exist under
|
|
14
|
+
// /sys/devices/system/edac/mc/, EDAC is not loaded (no memory
|
|
15
|
+
// controller driver compiled in or no DIMMs reporting) and this
|
|
16
|
+
// collector returns null. The dashboard ecc_errors evaluator falls
|
|
17
|
+
// back to its IPMI-SEL path.
|
|
18
|
+
//
|
|
19
|
+
// On hosts where EDAC is loaded but counters stay at zero, this
|
|
20
|
+
// collector emits zero counters — that's the correct baseline.
|
|
21
|
+
import { readdirSync, readFileSync } from "node:fs";
|
|
22
|
+
import { join } from "node:path";
|
|
23
|
+
const EDAC_ROOT = "/sys/devices/system/edac/mc";
|
|
24
|
+
function readUint(path) {
|
|
25
|
+
try {
|
|
26
|
+
const s = readFileSync(path, "utf8").trim();
|
|
27
|
+
if (!/^\d+$/.test(s))
|
|
28
|
+
return null;
|
|
29
|
+
return parseInt(s, 10);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function readString(path) {
|
|
36
|
+
try {
|
|
37
|
+
return readFileSync(path, "utf8").trim();
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function listMcDirs() {
|
|
44
|
+
try {
|
|
45
|
+
return readdirSync(EDAC_ROOT)
|
|
46
|
+
.filter((n) => /^mc\d+$/.test(n))
|
|
47
|
+
.map((n) => join(EDAC_ROOT, n));
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return [];
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function listDimmDirs(mcPath) {
|
|
54
|
+
try {
|
|
55
|
+
return readdirSync(mcPath)
|
|
56
|
+
.filter((n) => /^dimm\d+$/.test(n) || /^rank\d+$/.test(n))
|
|
57
|
+
.map((n) => join(mcPath, n));
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return [];
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function collectDimm(dimmPath) {
|
|
64
|
+
const ce = readUint(join(dimmPath, "dimm_ce_count"));
|
|
65
|
+
const ue = readUint(join(dimmPath, "dimm_ue_count"));
|
|
66
|
+
if (ce === null && ue === null)
|
|
67
|
+
return null;
|
|
68
|
+
const label = readString(join(dimmPath, "dimm_label"));
|
|
69
|
+
const location = readString(join(dimmPath, "dimm_location"));
|
|
70
|
+
const sizeStr = readString(join(dimmPath, "size"));
|
|
71
|
+
const sizeMb = sizeStr && /^\d+$/.test(sizeStr) ? parseInt(sizeStr, 10) : null;
|
|
72
|
+
return {
|
|
73
|
+
label: label ?? "",
|
|
74
|
+
location: location ?? "",
|
|
75
|
+
size_mb: sizeMb,
|
|
76
|
+
ce_count: ce ?? 0,
|
|
77
|
+
ue_count: ue ?? 0,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
export function collectEdac() {
|
|
81
|
+
const mcDirs = listMcDirs();
|
|
82
|
+
if (mcDirs.length === 0)
|
|
83
|
+
return null;
|
|
84
|
+
let totalCe = 0;
|
|
85
|
+
let totalUe = 0;
|
|
86
|
+
const dimms = [];
|
|
87
|
+
for (const mc of mcDirs) {
|
|
88
|
+
const ce = readUint(join(mc, "ce_count")) ?? 0;
|
|
89
|
+
const ue = readUint(join(mc, "ue_count")) ?? 0;
|
|
90
|
+
totalCe += ce;
|
|
91
|
+
totalUe += ue;
|
|
92
|
+
for (const dimm of listDimmDirs(mc)) {
|
|
93
|
+
const d = collectDimm(dimm);
|
|
94
|
+
if (d)
|
|
95
|
+
dimms.push(d);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return {
|
|
99
|
+
edac_corrected_total: totalCe,
|
|
100
|
+
edac_uncorrected_total: totalUe,
|
|
101
|
+
dimms,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=edac.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"edac.js","sourceRoot":"","sources":["../../src/collect/edac.ts"],"names":[],"mappings":"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"}
|
package/dist/collect/fd.d.ts
CHANGED
|
@@ -4,4 +4,50 @@ export interface FileDescriptorData {
|
|
|
4
4
|
max: number;
|
|
5
5
|
percent: number;
|
|
6
6
|
}
|
|
7
|
+
export interface ProcessFdEntry {
|
|
8
|
+
pid: number;
|
|
9
|
+
comm: string;
|
|
10
|
+
fd_count: number;
|
|
11
|
+
rlimit_nofile_soft: number;
|
|
12
|
+
rlimit_nofile_hard: number;
|
|
13
|
+
percent_of_soft_limit: number;
|
|
14
|
+
}
|
|
15
|
+
export interface ProcessFdSnapshot {
|
|
16
|
+
available: boolean;
|
|
17
|
+
reason?: string;
|
|
18
|
+
top_consumers: ProcessFdEntry[];
|
|
19
|
+
total_processes_scanned: number;
|
|
20
|
+
highest_percent_of_limit: number | null;
|
|
21
|
+
}
|
|
7
22
|
export declare function collectFileDescriptors(): FileDescriptorData;
|
|
23
|
+
/**
|
|
24
|
+
* Per-process FD scan. Walks /proc, counts FDs per PID, then reads
|
|
25
|
+
* `/proc/<pid>/limits` for the top-N consumers to compute proximity
|
|
26
|
+
* to each process's RLIMIT_NOFILE soft limit.
|
|
27
|
+
*
|
|
28
|
+
* Returns `{ available: false }` when /proc/1 is not a directory
|
|
29
|
+
* (essentially never on Linux; defensive).
|
|
30
|
+
*/
|
|
31
|
+
export declare function collectProcessFd(): ProcessFdSnapshot;
|
|
32
|
+
/**
|
|
33
|
+
* Parse the "Max open files" line from /proc/<pid>/limits.
|
|
34
|
+
*
|
|
35
|
+
* Format (fixed-width, header + rows):
|
|
36
|
+
* Limit Soft Limit Hard Limit Units
|
|
37
|
+
* Max open files 1024 4096 files
|
|
38
|
+
*
|
|
39
|
+
* Returns null when the line is missing or fields are unparseable.
|
|
40
|
+
* "unlimited" maps to Infinity which Number.MAX_SAFE_INTEGER would
|
|
41
|
+
* misrepresent; we use 0 as a sentinel meaning "no useful soft limit"
|
|
42
|
+
* which makes percent_of_soft_limit zero (and the dashboard rule
|
|
43
|
+
* treats that as a no-emission case).
|
|
44
|
+
*/
|
|
45
|
+
declare function parseOpenFilesLimit(raw: string): {
|
|
46
|
+
soft: number;
|
|
47
|
+
hard: number;
|
|
48
|
+
} | null;
|
|
49
|
+
export declare const __test_only: {
|
|
50
|
+
parseOpenFilesLimit: typeof parseOpenFilesLimit;
|
|
51
|
+
TOP_N: number;
|
|
52
|
+
};
|
|
53
|
+
export {};
|