@camstack/system 1.2.272 → 1.2.273
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/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
- package/dist/builtins/alerts/alerts.addon.js +1 -1
- package/dist/builtins/alerts/alerts.addon.mjs +1 -1
- package/dist/builtins/autotrack/index.js +1 -1
- package/dist/builtins/autotrack/index.mjs +1 -1
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +1 -1
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +1 -1
- package/dist/builtins/camera-grid/addon.d.ts +13 -0
- package/dist/builtins/camera-grid/grid-canvas-seed.d.ts +62 -0
- package/dist/builtins/camera-grid/grid-canvas.d.ts +4 -2
- package/dist/builtins/camera-grid/grid-child.d.ts +10 -0
- package/dist/builtins/camera-grid/grid-compositor.d.ts +10 -0
- package/dist/builtins/camera-grid/index.js +321 -10
- package/dist/builtins/camera-grid/index.mjs +321 -10
- package/dist/builtins/console-logging/index.js +1 -1
- package/dist/builtins/console-logging/index.mjs +1 -1
- package/dist/builtins/core-blocks/core-blocks.addon.js +1 -1
- package/dist/builtins/core-blocks/core-blocks.addon.mjs +1 -1
- package/dist/builtins/device-manager/device-manager.addon.js +2 -2
- package/dist/builtins/device-manager/device-manager.addon.mjs +2 -2
- package/dist/builtins/doorbell/virtual-doorbell.addon.js +1 -1
- package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +1 -1
- package/dist/builtins/hub-forwarder/index.js +1 -1
- package/dist/builtins/hub-forwarder/index.mjs +1 -1
- package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
- package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
- package/dist/builtins/local-auth/local-auth.addon.js +1 -1
- package/dist/builtins/local-auth/local-auth.addon.mjs +1 -1
- package/dist/builtins/local-network/local-network.addon.js +1 -1
- package/dist/builtins/local-network/local-network.addon.mjs +1 -1
- package/dist/builtins/loki-logging/index.js +1 -1
- package/dist/builtins/loki-logging/index.mjs +1 -1
- package/dist/builtins/native-metrics/cgroup-pids.d.ts +112 -0
- package/dist/builtins/native-metrics/native-metrics.addon.d.ts +1 -0
- package/dist/builtins/native-metrics/native-metrics.addon.js +209 -1
- package/dist/builtins/native-metrics/native-metrics.addon.mjs +209 -1
- package/dist/builtins/platform-probe/index.js +1 -1
- package/dist/builtins/platform-probe/index.mjs +1 -1
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
- package/dist/builtins/snapshot/index.js +1 -1
- package/dist/builtins/snapshot/index.mjs +1 -1
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +0 -0
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +0 -0
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
- package/dist/builtins/system-config/system-config.addon.js +1 -1
- package/dist/builtins/system-config/system-config.addon.mjs +1 -1
- package/dist/builtins/winston-logging/index.js +1 -1
- package/dist/builtins/winston-logging/index.mjs +1 -1
- package/dist/{dist-_YWIPtUH.js → dist-D5ONNqL6.js} +187 -11
- package/dist/{dist-Diz59Dhi.mjs → dist-DrLhdcwI.mjs} +187 -11
- package/dist/index.js +12 -5
- package/dist/index.mjs +12 -5
- package/dist/kernel/moleculer/process-service.d.ts +54 -0
- package/dist/{retired-settings-keys-C2ocoH_l.js → retired-settings-keys-1686ou3s.js} +1 -1
- package/dist/{retired-settings-keys-C_Hg5d37.mjs → retired-settings-keys-Du_0qedo.mjs} +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { IScopedLogger } from '@camstack/types';
|
|
2
|
+
import { CgroupFileReader } from './cgroup-memory.js';
|
|
3
|
+
/** Which hierarchy answered — reported so a reading is never ambiguous. */
|
|
4
|
+
export type CgroupPidsSource = 'cgroup-v2' | 'cgroup-v1';
|
|
5
|
+
/** One reading of the ceiling and our distance from it. */
|
|
6
|
+
export interface ContainerPidsSnapshot {
|
|
7
|
+
readonly source: CgroupPidsSource;
|
|
8
|
+
/** Threads (not processes) charged to this cgroup right now. */
|
|
9
|
+
readonly current: number;
|
|
10
|
+
/** The ceiling, or `null` when there is none. */
|
|
11
|
+
readonly max: number | null;
|
|
12
|
+
/** `current / max`, or `null` without a ceiling. */
|
|
13
|
+
readonly usedFraction: number | null;
|
|
14
|
+
/** Lifetime count of REFUSED forks, or `null` when the kernel does not say. */
|
|
15
|
+
readonly refusals: number | null;
|
|
16
|
+
}
|
|
17
|
+
/** Where the files for one hierarchy live, once resolved. */
|
|
18
|
+
export interface CgroupPidsLayout {
|
|
19
|
+
readonly source: CgroupPidsSource;
|
|
20
|
+
readonly directory: string;
|
|
21
|
+
readonly currentPath: string;
|
|
22
|
+
readonly maxPath: string;
|
|
23
|
+
readonly eventsPath: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* `pids.max` is either a number or the word `max`.
|
|
27
|
+
*
|
|
28
|
+
* `max` means NO ceiling, and that is `null` — not `Infinity`, not a huge
|
|
29
|
+
* number. A ceiling nothing can reach is not a ceiling, and reporting one
|
|
30
|
+
* would make every utilisation fraction on an unlimited node read as 0 %.
|
|
31
|
+
*/
|
|
32
|
+
export declare function parsePidsMax(text: string): number | null;
|
|
33
|
+
/**
|
|
34
|
+
* `pids.events` is `key value` per line; we read exactly one key:
|
|
35
|
+
*
|
|
36
|
+
* ```
|
|
37
|
+
* max 22
|
|
38
|
+
* ```
|
|
39
|
+
*
|
|
40
|
+
* `max` is the number of times a fork was refused because the limit was hit.
|
|
41
|
+
* A kernel that publishes only `pids.events.local`, or a file we cannot make
|
|
42
|
+
* sense of, answers `null` — unknown, never zero, because zero here means "no
|
|
43
|
+
* refusals have ever happened" and that is a claim.
|
|
44
|
+
*/
|
|
45
|
+
export declare function parsePidsEvents(text: string): number | null;
|
|
46
|
+
/** Turn one layout's files into a snapshot. Pure over the text. */
|
|
47
|
+
export declare function buildContainerPidsSnapshot(input: {
|
|
48
|
+
readonly source: CgroupPidsSource;
|
|
49
|
+
readonly currentText: string;
|
|
50
|
+
readonly maxText: string | null;
|
|
51
|
+
readonly eventsText: string | null;
|
|
52
|
+
}): ContainerPidsSnapshot | null;
|
|
53
|
+
/**
|
|
54
|
+
* Find the pids cgroup from INSIDE this process, never a hard-coded path —
|
|
55
|
+
* same reasoning as the memory layout: from inside the container the cgroup is
|
|
56
|
+
* at the mount ROOT (`0::/`), from the host it is `<mount>/docker/<id>`, and
|
|
57
|
+
* hard-coding either one breaks the other.
|
|
58
|
+
*/
|
|
59
|
+
export declare function resolveCgroupPidsLayout(fs: CgroupFileReader): CgroupPidsLayout | null;
|
|
60
|
+
export interface ContainerPidsProbeDeps {
|
|
61
|
+
readonly logger: IScopedLogger;
|
|
62
|
+
readonly fs?: CgroupFileReader;
|
|
63
|
+
readonly now?: () => number;
|
|
64
|
+
/** How often the heartbeat line is written. Default 60 s. */
|
|
65
|
+
readonly heartbeatMs?: number;
|
|
66
|
+
/** Utilisation at which the heartbeat becomes a WARN. Default 0.8. */
|
|
67
|
+
readonly pressureFraction?: number;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* The sampler.
|
|
71
|
+
*
|
|
72
|
+
* Its logging is deliberately different from the memory probe's, because the
|
|
73
|
+
* operator asked for a DAY of regular readings before deciding whether to
|
|
74
|
+
* raise the ceiling: there is a heartbeat, on its own cadence, whatever the
|
|
75
|
+
* sampling tick is. Three lines exist and no more:
|
|
76
|
+
*
|
|
77
|
+
* 1. **Resolution**, once — the hierarchy that answered, or "no cgroup here",
|
|
78
|
+
* which is a fact about the node and not an error.
|
|
79
|
+
* 2. **Heartbeat**, every `heartbeatMs` — current, ceiling, utilisation and
|
|
80
|
+
* the lifetime refusal count. INFO while there is room, WARN while we are
|
|
81
|
+
* above `pressureFraction`, and one INFO when it comes back down, so the
|
|
82
|
+
* log carries BOTH edges of a pressure episode rather than only its start.
|
|
83
|
+
* 3. **A refusal**, immediately — the `pids.events` counter rose, so the
|
|
84
|
+
* kernel said no that many times since the last sample and some subsystem
|
|
85
|
+
* silently did without a thread. This one never waits for the heartbeat.
|
|
86
|
+
*/
|
|
87
|
+
export declare class ContainerPidsProbe {
|
|
88
|
+
private readonly logger;
|
|
89
|
+
private readonly fs;
|
|
90
|
+
private readonly now;
|
|
91
|
+
private readonly heartbeatMs;
|
|
92
|
+
private readonly pressureFraction;
|
|
93
|
+
private resolved;
|
|
94
|
+
private layout;
|
|
95
|
+
private lastHeartbeatAt;
|
|
96
|
+
private lastRefusals;
|
|
97
|
+
private underPressure;
|
|
98
|
+
constructor(deps: ContainerPidsProbeDeps);
|
|
99
|
+
/** Did this node resolve a pids cgroup at all? Absent is a reportable fact. */
|
|
100
|
+
get available(): boolean;
|
|
101
|
+
/** One reading, or `null` where there is no cgroup or it stopped answering. */
|
|
102
|
+
sample(): ContainerPidsSnapshot | null;
|
|
103
|
+
private resolveOnce;
|
|
104
|
+
private report;
|
|
105
|
+
/**
|
|
106
|
+
* The counter only ever rises, so a rise is a refusal that happened in this
|
|
107
|
+
* window. Reported the moment it is seen — never held for the heartbeat,
|
|
108
|
+
* because this is the line that says work was lost.
|
|
109
|
+
*/
|
|
110
|
+
private reportRefusals;
|
|
111
|
+
private meta;
|
|
112
|
+
}
|
|
@@ -58,6 +58,7 @@ export default class NativeMetricsAddon extends BaseAddon<NativeMetricsConfig> {
|
|
|
58
58
|
* cgroup once and latches.
|
|
59
59
|
*/
|
|
60
60
|
private memoryProbe;
|
|
61
|
+
private pidsProbe;
|
|
61
62
|
/** Hot tier for the container reading. Bounds in `container-memory-series.ts`. */
|
|
62
63
|
private readonly memoryRing;
|
|
63
64
|
/** Cold tier, HUB only — same reasoning as `loadStore`. */
|
|
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
const require_chunk = require("../../chunk-Cek0wNdY.js");
|
|
6
|
-
const require_dist = require("../../dist-
|
|
6
|
+
const require_dist = require("../../dist-D5ONNqL6.js");
|
|
7
7
|
let node_fs_promises = require("node:fs/promises");
|
|
8
8
|
let node_fs = require("node:fs");
|
|
9
9
|
node_fs = require_chunk.__toESM(node_fs);
|
|
@@ -350,6 +350,211 @@ var ContainerMemoryProbe = class {
|
|
|
350
350
|
if (!first) this.logger.info("cgroup memory is answering again");
|
|
351
351
|
}
|
|
352
352
|
};
|
|
353
|
+
//#endregion
|
|
354
|
+
//#region src/builtins/native-metrics/cgroup-pids.ts
|
|
355
|
+
/**
|
|
356
|
+
* `pids.max` is either a number or the word `max`.
|
|
357
|
+
*
|
|
358
|
+
* `max` means NO ceiling, and that is `null` — not `Infinity`, not a huge
|
|
359
|
+
* number. A ceiling nothing can reach is not a ceiling, and reporting one
|
|
360
|
+
* would make every utilisation fraction on an unlimited node read as 0 %.
|
|
361
|
+
*/
|
|
362
|
+
function parsePidsMax(text) {
|
|
363
|
+
const trimmed = text.trim();
|
|
364
|
+
if (trimmed.length === 0 || trimmed === "max") return null;
|
|
365
|
+
const value = Number(trimmed);
|
|
366
|
+
if (!Number.isFinite(value) || value < 0) return null;
|
|
367
|
+
return value;
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* `pids.events` is `key value` per line; we read exactly one key:
|
|
371
|
+
*
|
|
372
|
+
* ```
|
|
373
|
+
* max 22
|
|
374
|
+
* ```
|
|
375
|
+
*
|
|
376
|
+
* `max` is the number of times a fork was refused because the limit was hit.
|
|
377
|
+
* A kernel that publishes only `pids.events.local`, or a file we cannot make
|
|
378
|
+
* sense of, answers `null` — unknown, never zero, because zero here means "no
|
|
379
|
+
* refusals have ever happened" and that is a claim.
|
|
380
|
+
*/
|
|
381
|
+
function parsePidsEvents(text) {
|
|
382
|
+
for (const rawLine of text.split("\n")) {
|
|
383
|
+
const line = rawLine.trim();
|
|
384
|
+
if (line.length === 0) continue;
|
|
385
|
+
const space = line.indexOf(" ");
|
|
386
|
+
if (space <= 0) continue;
|
|
387
|
+
if (line.slice(0, space) !== "max") continue;
|
|
388
|
+
const value = Number(line.slice(space + 1).trim());
|
|
389
|
+
if (!Number.isFinite(value) || value < 0) return null;
|
|
390
|
+
return value;
|
|
391
|
+
}
|
|
392
|
+
return null;
|
|
393
|
+
}
|
|
394
|
+
/** Turn one layout's files into a snapshot. Pure over the text. */
|
|
395
|
+
function buildContainerPidsSnapshot(input) {
|
|
396
|
+
const current = Number(input.currentText.trim());
|
|
397
|
+
if (!Number.isFinite(current) || current < 0) return null;
|
|
398
|
+
const max = input.maxText === null ? null : parsePidsMax(input.maxText);
|
|
399
|
+
return {
|
|
400
|
+
source: input.source,
|
|
401
|
+
current,
|
|
402
|
+
max,
|
|
403
|
+
usedFraction: max === null || max === 0 ? null : current / max,
|
|
404
|
+
refusals: input.eventsText === null ? null : parsePidsEvents(input.eventsText)
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Find the pids cgroup from INSIDE this process, never a hard-coded path —
|
|
409
|
+
* same reasoning as the memory layout: from inside the container the cgroup is
|
|
410
|
+
* at the mount ROOT (`0::/`), from the host it is `<mount>/docker/<id>`, and
|
|
411
|
+
* hard-coding either one breaks the other.
|
|
412
|
+
*/
|
|
413
|
+
function resolveCgroupPidsLayout(fs) {
|
|
414
|
+
const selfText = fs.read("/proc/self/cgroup");
|
|
415
|
+
if (selfText === null) return null;
|
|
416
|
+
const entry = parseCgroupSelf(selfText);
|
|
417
|
+
if (entry === null) return null;
|
|
418
|
+
const suffix = entry.path === "/" ? "" : entry.path;
|
|
419
|
+
const candidates = entry.version === "v2" ? [`${CGROUP_MOUNT_ROOT}${suffix}`, CGROUP_MOUNT_ROOT] : [`${CGROUP_MOUNT_ROOT}/pids${suffix}`, `${CGROUP_MOUNT_ROOT}/pids`];
|
|
420
|
+
for (const directory of candidates) {
|
|
421
|
+
if (fs.read(`${directory}/pids.current`) === null) continue;
|
|
422
|
+
return {
|
|
423
|
+
source: entry.version === "v2" ? "cgroup-v2" : "cgroup-v1",
|
|
424
|
+
directory,
|
|
425
|
+
currentPath: `${directory}/pids.current`,
|
|
426
|
+
maxPath: `${directory}/pids.max`,
|
|
427
|
+
eventsPath: `${directory}/pids.events`
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
return null;
|
|
431
|
+
}
|
|
432
|
+
var DEFAULT_HEARTBEAT_MS = 6e4;
|
|
433
|
+
var DEFAULT_PRESSURE_FRACTION = .8;
|
|
434
|
+
/**
|
|
435
|
+
* The sampler.
|
|
436
|
+
*
|
|
437
|
+
* Its logging is deliberately different from the memory probe's, because the
|
|
438
|
+
* operator asked for a DAY of regular readings before deciding whether to
|
|
439
|
+
* raise the ceiling: there is a heartbeat, on its own cadence, whatever the
|
|
440
|
+
* sampling tick is. Three lines exist and no more:
|
|
441
|
+
*
|
|
442
|
+
* 1. **Resolution**, once — the hierarchy that answered, or "no cgroup here",
|
|
443
|
+
* which is a fact about the node and not an error.
|
|
444
|
+
* 2. **Heartbeat**, every `heartbeatMs` — current, ceiling, utilisation and
|
|
445
|
+
* the lifetime refusal count. INFO while there is room, WARN while we are
|
|
446
|
+
* above `pressureFraction`, and one INFO when it comes back down, so the
|
|
447
|
+
* log carries BOTH edges of a pressure episode rather than only its start.
|
|
448
|
+
* 3. **A refusal**, immediately — the `pids.events` counter rose, so the
|
|
449
|
+
* kernel said no that many times since the last sample and some subsystem
|
|
450
|
+
* silently did without a thread. This one never waits for the heartbeat.
|
|
451
|
+
*/
|
|
452
|
+
var ContainerPidsProbe = class {
|
|
453
|
+
logger;
|
|
454
|
+
fs;
|
|
455
|
+
now;
|
|
456
|
+
heartbeatMs;
|
|
457
|
+
pressureFraction;
|
|
458
|
+
resolved = false;
|
|
459
|
+
layout = null;
|
|
460
|
+
lastHeartbeatAt = null;
|
|
461
|
+
lastRefusals = null;
|
|
462
|
+
underPressure = false;
|
|
463
|
+
constructor(deps) {
|
|
464
|
+
this.logger = deps.logger;
|
|
465
|
+
this.fs = deps.fs ?? nodeCgroupFileReader;
|
|
466
|
+
this.now = deps.now ?? Date.now;
|
|
467
|
+
this.heartbeatMs = deps.heartbeatMs ?? DEFAULT_HEARTBEAT_MS;
|
|
468
|
+
this.pressureFraction = deps.pressureFraction ?? DEFAULT_PRESSURE_FRACTION;
|
|
469
|
+
}
|
|
470
|
+
/** Did this node resolve a pids cgroup at all? Absent is a reportable fact. */
|
|
471
|
+
get available() {
|
|
472
|
+
return this.layout !== null;
|
|
473
|
+
}
|
|
474
|
+
/** One reading, or `null` where there is no cgroup or it stopped answering. */
|
|
475
|
+
sample() {
|
|
476
|
+
const layout = this.resolveOnce();
|
|
477
|
+
if (layout === null) return null;
|
|
478
|
+
const currentText = this.fs.read(layout.currentPath);
|
|
479
|
+
if (currentText === null) return null;
|
|
480
|
+
const snapshot = buildContainerPidsSnapshot({
|
|
481
|
+
source: layout.source,
|
|
482
|
+
currentText,
|
|
483
|
+
maxText: this.fs.read(layout.maxPath),
|
|
484
|
+
eventsText: this.fs.read(layout.eventsPath)
|
|
485
|
+
});
|
|
486
|
+
if (snapshot === null) return null;
|
|
487
|
+
this.report(snapshot);
|
|
488
|
+
return snapshot;
|
|
489
|
+
}
|
|
490
|
+
resolveOnce() {
|
|
491
|
+
if (this.resolved) return this.layout;
|
|
492
|
+
this.resolved = true;
|
|
493
|
+
try {
|
|
494
|
+
this.layout = resolveCgroupPidsLayout(this.fs);
|
|
495
|
+
} catch (err) {
|
|
496
|
+
this.layout = null;
|
|
497
|
+
this.logger.warn("cgroup pids resolution failed — the pid ceiling will be ABSENT", { meta: { error: require_dist.errMsg(err) } });
|
|
498
|
+
return null;
|
|
499
|
+
}
|
|
500
|
+
if (this.layout === null) {
|
|
501
|
+
this.logger.info("no pids cgroup on this node — the pid ceiling is ABSENT, not unlimited, for every sample", { meta: {
|
|
502
|
+
platform: process.platform,
|
|
503
|
+
probed: "/proc/self/cgroup"
|
|
504
|
+
} });
|
|
505
|
+
return null;
|
|
506
|
+
}
|
|
507
|
+
this.logger.info("pid ceiling resolved from the cgroup", { meta: {
|
|
508
|
+
source: this.layout.source,
|
|
509
|
+
directory: this.layout.directory
|
|
510
|
+
} });
|
|
511
|
+
return this.layout;
|
|
512
|
+
}
|
|
513
|
+
report(snapshot) {
|
|
514
|
+
this.reportRefusals(snapshot);
|
|
515
|
+
const at = this.now();
|
|
516
|
+
const pressure = snapshot.usedFraction !== null && snapshot.usedFraction >= this.pressureFraction;
|
|
517
|
+
if (this.underPressure && !pressure) {
|
|
518
|
+
this.underPressure = false;
|
|
519
|
+
this.logger.info("pid pressure recovered — back under the guard fraction", { meta: this.meta(snapshot) });
|
|
520
|
+
this.lastHeartbeatAt = at;
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
if (!(this.lastHeartbeatAt === null || at - this.lastHeartbeatAt >= this.heartbeatMs)) return;
|
|
524
|
+
this.lastHeartbeatAt = at;
|
|
525
|
+
this.underPressure = pressure;
|
|
526
|
+
if (pressure) {
|
|
527
|
+
this.logger.warn("pid ceiling is being approached — a refused clone() is silent work loss, and this container counts THREADS", { meta: this.meta(snapshot) });
|
|
528
|
+
return;
|
|
529
|
+
}
|
|
530
|
+
this.logger.info("pid ceiling", { meta: this.meta(snapshot) });
|
|
531
|
+
}
|
|
532
|
+
/**
|
|
533
|
+
* The counter only ever rises, so a rise is a refusal that happened in this
|
|
534
|
+
* window. Reported the moment it is seen — never held for the heartbeat,
|
|
535
|
+
* because this is the line that says work was lost.
|
|
536
|
+
*/
|
|
537
|
+
reportRefusals(snapshot) {
|
|
538
|
+
const refusals = snapshot.refusals;
|
|
539
|
+
if (refusals === null) return;
|
|
540
|
+
const previous = this.lastRefusals;
|
|
541
|
+
this.lastRefusals = refusals;
|
|
542
|
+
if (previous === null || refusals <= previous) return;
|
|
543
|
+
this.logger.warn("the kernel REFUSED a fork at the pid ceiling — something asked for a thread and did not get one", { meta: {
|
|
544
|
+
...this.meta(snapshot),
|
|
545
|
+
refusedSinceLastSample: refusals - previous
|
|
546
|
+
} });
|
|
547
|
+
}
|
|
548
|
+
meta(snapshot) {
|
|
549
|
+
return {
|
|
550
|
+
current: snapshot.current,
|
|
551
|
+
max: snapshot.max,
|
|
552
|
+
usedPct: snapshot.usedFraction === null ? null : Math.round(snapshot.usedFraction * 1e3) / 10,
|
|
553
|
+
refusalsLifetime: snapshot.refusals,
|
|
554
|
+
source: snapshot.source
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
};
|
|
353
558
|
/**
|
|
354
559
|
* One node's fixed-capacity circular buffer of samples.
|
|
355
560
|
*
|
|
@@ -2809,6 +3014,7 @@ var NativeMetricsAddon = class NativeMetricsAddon extends require_dist.BaseAddon
|
|
|
2809
3014
|
* cgroup once and latches.
|
|
2810
3015
|
*/
|
|
2811
3016
|
memoryProbe = null;
|
|
3017
|
+
pidsProbe = null;
|
|
2812
3018
|
/** Hot tier for the container reading. Bounds in `container-memory-series.ts`. */
|
|
2813
3019
|
memoryRing = new ContainerMemoryRing();
|
|
2814
3020
|
/** Cold tier, HUB only — same reasoning as `loadStore`. */
|
|
@@ -2895,6 +3101,7 @@ var NativeMetricsAddon = class NativeMetricsAddon extends require_dist.BaseAddon
|
|
|
2895
3101
|
});
|
|
2896
3102
|
}
|
|
2897
3103
|
this.memoryProbe = new ContainerMemoryProbe({ logger: this.ctx.logger.child("ContainerMemory") });
|
|
3104
|
+
this.pidsProbe = new ContainerPidsProbe({ logger: this.ctx.logger.child("ContainerPids") });
|
|
2898
3105
|
this.snapshotTimer = setInterval(() => this.emitResourcesSnapshot(), METRICS_SNAPSHOT_INTERVAL_MS);
|
|
2899
3106
|
this.startProcessSnapshotTimer();
|
|
2900
3107
|
this.ctx.addDisposer(this.ctx.eventBus.subscribe({ category: require_dist.EventCategory.MetricsNodeProcessesSnapshot }, (event) => this.retainSnapshot(event.data.nodeId, event.data.timestamp, event.data.processes, event.data.containerMemory)));
|
|
@@ -3135,6 +3342,7 @@ var NativeMetricsAddon = class NativeMetricsAddon extends require_dist.BaseAddon
|
|
|
3135
3342
|
try {
|
|
3136
3343
|
const processes = await this.listNodeProcesses();
|
|
3137
3344
|
const containerMemory = this.memoryProbe?.sample() ?? null;
|
|
3345
|
+
this.pidsProbe?.sample();
|
|
3138
3346
|
eventBus.emit(require_dist.createEvent(require_dist.EventCategory.MetricsNodeProcessesSnapshot, {
|
|
3139
3347
|
type: "node",
|
|
3140
3348
|
id: nodeId,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Lt as reducePoints, Ot as metricsProviderCapability, Pn as EventCategory, Rt as resolveBucketMs, an as errMsg, bn as createEvent, lt as foldSnapshotByFunction, sn as BaseAddon } from "../../dist-
|
|
1
|
+
import { Lt as reducePoints, Ot as metricsProviderCapability, Pn as EventCategory, Rt as resolveBucketMs, an as errMsg, bn as createEvent, lt as foldSnapshotByFunction, sn as BaseAddon } from "../../dist-DrLhdcwI.mjs";
|
|
2
2
|
import { access, readFile, readdir } from "node:fs/promises";
|
|
3
3
|
import * as fs from "node:fs";
|
|
4
4
|
import { constants, readFileSync } from "node:fs";
|
|
@@ -345,6 +345,211 @@ var ContainerMemoryProbe = class {
|
|
|
345
345
|
if (!first) this.logger.info("cgroup memory is answering again");
|
|
346
346
|
}
|
|
347
347
|
};
|
|
348
|
+
//#endregion
|
|
349
|
+
//#region src/builtins/native-metrics/cgroup-pids.ts
|
|
350
|
+
/**
|
|
351
|
+
* `pids.max` is either a number or the word `max`.
|
|
352
|
+
*
|
|
353
|
+
* `max` means NO ceiling, and that is `null` — not `Infinity`, not a huge
|
|
354
|
+
* number. A ceiling nothing can reach is not a ceiling, and reporting one
|
|
355
|
+
* would make every utilisation fraction on an unlimited node read as 0 %.
|
|
356
|
+
*/
|
|
357
|
+
function parsePidsMax(text) {
|
|
358
|
+
const trimmed = text.trim();
|
|
359
|
+
if (trimmed.length === 0 || trimmed === "max") return null;
|
|
360
|
+
const value = Number(trimmed);
|
|
361
|
+
if (!Number.isFinite(value) || value < 0) return null;
|
|
362
|
+
return value;
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* `pids.events` is `key value` per line; we read exactly one key:
|
|
366
|
+
*
|
|
367
|
+
* ```
|
|
368
|
+
* max 22
|
|
369
|
+
* ```
|
|
370
|
+
*
|
|
371
|
+
* `max` is the number of times a fork was refused because the limit was hit.
|
|
372
|
+
* A kernel that publishes only `pids.events.local`, or a file we cannot make
|
|
373
|
+
* sense of, answers `null` — unknown, never zero, because zero here means "no
|
|
374
|
+
* refusals have ever happened" and that is a claim.
|
|
375
|
+
*/
|
|
376
|
+
function parsePidsEvents(text) {
|
|
377
|
+
for (const rawLine of text.split("\n")) {
|
|
378
|
+
const line = rawLine.trim();
|
|
379
|
+
if (line.length === 0) continue;
|
|
380
|
+
const space = line.indexOf(" ");
|
|
381
|
+
if (space <= 0) continue;
|
|
382
|
+
if (line.slice(0, space) !== "max") continue;
|
|
383
|
+
const value = Number(line.slice(space + 1).trim());
|
|
384
|
+
if (!Number.isFinite(value) || value < 0) return null;
|
|
385
|
+
return value;
|
|
386
|
+
}
|
|
387
|
+
return null;
|
|
388
|
+
}
|
|
389
|
+
/** Turn one layout's files into a snapshot. Pure over the text. */
|
|
390
|
+
function buildContainerPidsSnapshot(input) {
|
|
391
|
+
const current = Number(input.currentText.trim());
|
|
392
|
+
if (!Number.isFinite(current) || current < 0) return null;
|
|
393
|
+
const max = input.maxText === null ? null : parsePidsMax(input.maxText);
|
|
394
|
+
return {
|
|
395
|
+
source: input.source,
|
|
396
|
+
current,
|
|
397
|
+
max,
|
|
398
|
+
usedFraction: max === null || max === 0 ? null : current / max,
|
|
399
|
+
refusals: input.eventsText === null ? null : parsePidsEvents(input.eventsText)
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Find the pids cgroup from INSIDE this process, never a hard-coded path —
|
|
404
|
+
* same reasoning as the memory layout: from inside the container the cgroup is
|
|
405
|
+
* at the mount ROOT (`0::/`), from the host it is `<mount>/docker/<id>`, and
|
|
406
|
+
* hard-coding either one breaks the other.
|
|
407
|
+
*/
|
|
408
|
+
function resolveCgroupPidsLayout(fs) {
|
|
409
|
+
const selfText = fs.read("/proc/self/cgroup");
|
|
410
|
+
if (selfText === null) return null;
|
|
411
|
+
const entry = parseCgroupSelf(selfText);
|
|
412
|
+
if (entry === null) return null;
|
|
413
|
+
const suffix = entry.path === "/" ? "" : entry.path;
|
|
414
|
+
const candidates = entry.version === "v2" ? [`${CGROUP_MOUNT_ROOT}${suffix}`, CGROUP_MOUNT_ROOT] : [`${CGROUP_MOUNT_ROOT}/pids${suffix}`, `${CGROUP_MOUNT_ROOT}/pids`];
|
|
415
|
+
for (const directory of candidates) {
|
|
416
|
+
if (fs.read(`${directory}/pids.current`) === null) continue;
|
|
417
|
+
return {
|
|
418
|
+
source: entry.version === "v2" ? "cgroup-v2" : "cgroup-v1",
|
|
419
|
+
directory,
|
|
420
|
+
currentPath: `${directory}/pids.current`,
|
|
421
|
+
maxPath: `${directory}/pids.max`,
|
|
422
|
+
eventsPath: `${directory}/pids.events`
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
return null;
|
|
426
|
+
}
|
|
427
|
+
var DEFAULT_HEARTBEAT_MS = 6e4;
|
|
428
|
+
var DEFAULT_PRESSURE_FRACTION = .8;
|
|
429
|
+
/**
|
|
430
|
+
* The sampler.
|
|
431
|
+
*
|
|
432
|
+
* Its logging is deliberately different from the memory probe's, because the
|
|
433
|
+
* operator asked for a DAY of regular readings before deciding whether to
|
|
434
|
+
* raise the ceiling: there is a heartbeat, on its own cadence, whatever the
|
|
435
|
+
* sampling tick is. Three lines exist and no more:
|
|
436
|
+
*
|
|
437
|
+
* 1. **Resolution**, once — the hierarchy that answered, or "no cgroup here",
|
|
438
|
+
* which is a fact about the node and not an error.
|
|
439
|
+
* 2. **Heartbeat**, every `heartbeatMs` — current, ceiling, utilisation and
|
|
440
|
+
* the lifetime refusal count. INFO while there is room, WARN while we are
|
|
441
|
+
* above `pressureFraction`, and one INFO when it comes back down, so the
|
|
442
|
+
* log carries BOTH edges of a pressure episode rather than only its start.
|
|
443
|
+
* 3. **A refusal**, immediately — the `pids.events` counter rose, so the
|
|
444
|
+
* kernel said no that many times since the last sample and some subsystem
|
|
445
|
+
* silently did without a thread. This one never waits for the heartbeat.
|
|
446
|
+
*/
|
|
447
|
+
var ContainerPidsProbe = class {
|
|
448
|
+
logger;
|
|
449
|
+
fs;
|
|
450
|
+
now;
|
|
451
|
+
heartbeatMs;
|
|
452
|
+
pressureFraction;
|
|
453
|
+
resolved = false;
|
|
454
|
+
layout = null;
|
|
455
|
+
lastHeartbeatAt = null;
|
|
456
|
+
lastRefusals = null;
|
|
457
|
+
underPressure = false;
|
|
458
|
+
constructor(deps) {
|
|
459
|
+
this.logger = deps.logger;
|
|
460
|
+
this.fs = deps.fs ?? nodeCgroupFileReader;
|
|
461
|
+
this.now = deps.now ?? Date.now;
|
|
462
|
+
this.heartbeatMs = deps.heartbeatMs ?? DEFAULT_HEARTBEAT_MS;
|
|
463
|
+
this.pressureFraction = deps.pressureFraction ?? DEFAULT_PRESSURE_FRACTION;
|
|
464
|
+
}
|
|
465
|
+
/** Did this node resolve a pids cgroup at all? Absent is a reportable fact. */
|
|
466
|
+
get available() {
|
|
467
|
+
return this.layout !== null;
|
|
468
|
+
}
|
|
469
|
+
/** One reading, or `null` where there is no cgroup or it stopped answering. */
|
|
470
|
+
sample() {
|
|
471
|
+
const layout = this.resolveOnce();
|
|
472
|
+
if (layout === null) return null;
|
|
473
|
+
const currentText = this.fs.read(layout.currentPath);
|
|
474
|
+
if (currentText === null) return null;
|
|
475
|
+
const snapshot = buildContainerPidsSnapshot({
|
|
476
|
+
source: layout.source,
|
|
477
|
+
currentText,
|
|
478
|
+
maxText: this.fs.read(layout.maxPath),
|
|
479
|
+
eventsText: this.fs.read(layout.eventsPath)
|
|
480
|
+
});
|
|
481
|
+
if (snapshot === null) return null;
|
|
482
|
+
this.report(snapshot);
|
|
483
|
+
return snapshot;
|
|
484
|
+
}
|
|
485
|
+
resolveOnce() {
|
|
486
|
+
if (this.resolved) return this.layout;
|
|
487
|
+
this.resolved = true;
|
|
488
|
+
try {
|
|
489
|
+
this.layout = resolveCgroupPidsLayout(this.fs);
|
|
490
|
+
} catch (err) {
|
|
491
|
+
this.layout = null;
|
|
492
|
+
this.logger.warn("cgroup pids resolution failed — the pid ceiling will be ABSENT", { meta: { error: errMsg(err) } });
|
|
493
|
+
return null;
|
|
494
|
+
}
|
|
495
|
+
if (this.layout === null) {
|
|
496
|
+
this.logger.info("no pids cgroup on this node — the pid ceiling is ABSENT, not unlimited, for every sample", { meta: {
|
|
497
|
+
platform: process.platform,
|
|
498
|
+
probed: "/proc/self/cgroup"
|
|
499
|
+
} });
|
|
500
|
+
return null;
|
|
501
|
+
}
|
|
502
|
+
this.logger.info("pid ceiling resolved from the cgroup", { meta: {
|
|
503
|
+
source: this.layout.source,
|
|
504
|
+
directory: this.layout.directory
|
|
505
|
+
} });
|
|
506
|
+
return this.layout;
|
|
507
|
+
}
|
|
508
|
+
report(snapshot) {
|
|
509
|
+
this.reportRefusals(snapshot);
|
|
510
|
+
const at = this.now();
|
|
511
|
+
const pressure = snapshot.usedFraction !== null && snapshot.usedFraction >= this.pressureFraction;
|
|
512
|
+
if (this.underPressure && !pressure) {
|
|
513
|
+
this.underPressure = false;
|
|
514
|
+
this.logger.info("pid pressure recovered — back under the guard fraction", { meta: this.meta(snapshot) });
|
|
515
|
+
this.lastHeartbeatAt = at;
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
if (!(this.lastHeartbeatAt === null || at - this.lastHeartbeatAt >= this.heartbeatMs)) return;
|
|
519
|
+
this.lastHeartbeatAt = at;
|
|
520
|
+
this.underPressure = pressure;
|
|
521
|
+
if (pressure) {
|
|
522
|
+
this.logger.warn("pid ceiling is being approached — a refused clone() is silent work loss, and this container counts THREADS", { meta: this.meta(snapshot) });
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
this.logger.info("pid ceiling", { meta: this.meta(snapshot) });
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* The counter only ever rises, so a rise is a refusal that happened in this
|
|
529
|
+
* window. Reported the moment it is seen — never held for the heartbeat,
|
|
530
|
+
* because this is the line that says work was lost.
|
|
531
|
+
*/
|
|
532
|
+
reportRefusals(snapshot) {
|
|
533
|
+
const refusals = snapshot.refusals;
|
|
534
|
+
if (refusals === null) return;
|
|
535
|
+
const previous = this.lastRefusals;
|
|
536
|
+
this.lastRefusals = refusals;
|
|
537
|
+
if (previous === null || refusals <= previous) return;
|
|
538
|
+
this.logger.warn("the kernel REFUSED a fork at the pid ceiling — something asked for a thread and did not get one", { meta: {
|
|
539
|
+
...this.meta(snapshot),
|
|
540
|
+
refusedSinceLastSample: refusals - previous
|
|
541
|
+
} });
|
|
542
|
+
}
|
|
543
|
+
meta(snapshot) {
|
|
544
|
+
return {
|
|
545
|
+
current: snapshot.current,
|
|
546
|
+
max: snapshot.max,
|
|
547
|
+
usedPct: snapshot.usedFraction === null ? null : Math.round(snapshot.usedFraction * 1e3) / 10,
|
|
548
|
+
refusalsLifetime: snapshot.refusals,
|
|
549
|
+
source: snapshot.source
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
};
|
|
348
553
|
/**
|
|
349
554
|
* One node's fixed-capacity circular buffer of samples.
|
|
350
555
|
*
|
|
@@ -2804,6 +3009,7 @@ var NativeMetricsAddon = class NativeMetricsAddon extends BaseAddon {
|
|
|
2804
3009
|
* cgroup once and latches.
|
|
2805
3010
|
*/
|
|
2806
3011
|
memoryProbe = null;
|
|
3012
|
+
pidsProbe = null;
|
|
2807
3013
|
/** Hot tier for the container reading. Bounds in `container-memory-series.ts`. */
|
|
2808
3014
|
memoryRing = new ContainerMemoryRing();
|
|
2809
3015
|
/** Cold tier, HUB only — same reasoning as `loadStore`. */
|
|
@@ -2890,6 +3096,7 @@ var NativeMetricsAddon = class NativeMetricsAddon extends BaseAddon {
|
|
|
2890
3096
|
});
|
|
2891
3097
|
}
|
|
2892
3098
|
this.memoryProbe = new ContainerMemoryProbe({ logger: this.ctx.logger.child("ContainerMemory") });
|
|
3099
|
+
this.pidsProbe = new ContainerPidsProbe({ logger: this.ctx.logger.child("ContainerPids") });
|
|
2893
3100
|
this.snapshotTimer = setInterval(() => this.emitResourcesSnapshot(), METRICS_SNAPSHOT_INTERVAL_MS);
|
|
2894
3101
|
this.startProcessSnapshotTimer();
|
|
2895
3102
|
this.ctx.addDisposer(this.ctx.eventBus.subscribe({ category: EventCategory.MetricsNodeProcessesSnapshot }, (event) => this.retainSnapshot(event.data.nodeId, event.data.timestamp, event.data.processes, event.data.containerMemory)));
|
|
@@ -3130,6 +3337,7 @@ var NativeMetricsAddon = class NativeMetricsAddon extends BaseAddon {
|
|
|
3130
3337
|
try {
|
|
3131
3338
|
const processes = await this.listNodeProcesses();
|
|
3132
3339
|
const containerMemory = this.memoryProbe?.sample() ?? null;
|
|
3340
|
+
this.pidsProbe?.sample();
|
|
3133
3341
|
eventBus.emit(createEvent(EventCategory.MetricsNodeProcessesSnapshot, {
|
|
3134
3342
|
type: "node",
|
|
3135
3343
|
id: nodeId,
|
|
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
const require_chunk = require("../../chunk-Cek0wNdY.js");
|
|
6
|
-
const require_dist = require("../../dist-
|
|
6
|
+
const require_dist = require("../../dist-D5ONNqL6.js");
|
|
7
7
|
let node_fs = require("node:fs");
|
|
8
8
|
node_fs = require_chunk.__toESM(node_fs);
|
|
9
9
|
let node_child_process = require("node:child_process");
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { o as __toESM } from "../../chunk-CNf5ZN-e.mjs";
|
|
2
|
-
import { Gt as scoreRuntimes, Pn as EventCategory, Pt as platformProbeCapability, Sn as emitReadiness, an as errMsg, nt as enumerateInferenceDevices, sn as BaseAddon } from "../../dist-
|
|
2
|
+
import { Gt as scoreRuntimes, Pn as EventCategory, Pt as platformProbeCapability, Sn as emitReadiness, an as errMsg, nt as enumerateInferenceDevices, sn as BaseAddon } from "../../dist-DrLhdcwI.mjs";
|
|
3
3
|
import * as fs from "node:fs";
|
|
4
4
|
import { execFile } from "node:child_process";
|
|
5
5
|
import * as os from "node:os";
|
|
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
require("../../chunk-Cek0wNdY.js");
|
|
6
|
-
const require_dist = require("../../dist-
|
|
6
|
+
const require_dist = require("../../dist-D5ONNqL6.js");
|
|
7
7
|
//#region src/builtins/remote-access-orchestrator/enabled-providers-reconcile.ts
|
|
8
8
|
/**
|
|
9
9
|
* Reconcile the durable `enabledProviders` set against authoritative
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Pn as EventCategory, sn as BaseAddon } from "../../dist-
|
|
1
|
+
import { Pn as EventCategory, sn as BaseAddon } from "../../dist-DrLhdcwI.mjs";
|
|
2
2
|
//#region src/builtins/remote-access-orchestrator/enabled-providers-reconcile.ts
|
|
3
3
|
/**
|
|
4
4
|
* Reconcile the durable `enabledProviders` set against authoritative
|
|
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
const require_chunk = require("../../chunk-Cek0wNdY.js");
|
|
6
|
-
const require_dist = require("../../dist-
|
|
6
|
+
const require_dist = require("../../dist-D5ONNqL6.js");
|
|
7
7
|
const require_snapshot_courtesy = require("../../snapshot-courtesy-Cqs7GkOV.js");
|
|
8
8
|
let zod = require("zod");
|
|
9
9
|
let node_crypto = require("node:crypto");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { B as bareAddonId, Dn as nodePin, Jt as snapshotCapability, Pn as EventCategory, V as batteryCapability, Z as deriveBatteryPresence, a as BatteryStatusSchema, an as errMsg, dn as DeviceFeature, en as streamQualityLabel, pn as DeviceType, sn as BaseAddon } from "../../dist-
|
|
1
|
+
import { B as bareAddonId, Dn as nodePin, Jt as snapshotCapability, Pn as EventCategory, V as batteryCapability, Z as deriveBatteryPresence, a as BatteryStatusSchema, an as errMsg, dn as DeviceFeature, en as streamQualityLabel, pn as DeviceType, sn as BaseAddon } from "../../dist-DrLhdcwI.mjs";
|
|
2
2
|
import { i as renderCourtesyOverlayJpeg, n as courtesyCacheKey, r as renderCourtesyJpeg } from "../../snapshot-courtesy-CZPvteq2.mjs";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { randomUUID } from "node:crypto";
|
|
@@ -3,7 +3,7 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
const require_chunk = require("../../chunk-Cek0wNdY.js");
|
|
6
|
-
const require_dist = require("../../dist-
|
|
6
|
+
const require_dist = require("../../dist-D5ONNqL6.js");
|
|
7
7
|
let node_crypto = require("node:crypto");
|
|
8
8
|
let node_fs_promises = require("node:fs/promises");
|
|
9
9
|
let node_path = require("node:path");
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Qt as storageProviderCapability, ct as filesystemBrowseCapability, sn as BaseAddon } from "../../dist-
|
|
1
|
+
import { Qt as storageProviderCapability, ct as filesystemBrowseCapability, sn as BaseAddon } from "../../dist-DrLhdcwI.mjs";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
import { mkdir, readdir, realpath, statfs } from "node:fs/promises";
|
|
4
4
|
import * as path$1 from "node:path";
|
|
Binary file
|
|
Binary file
|