@glassmkr/crucible 0.6.5 → 0.7.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/.dockerignore ADDED
@@ -0,0 +1,13 @@
1
+ node_modules
2
+ .git
3
+ .github
4
+ *.md
5
+ !README.md
6
+ .svelte-kit
7
+ dist
8
+ coverage
9
+ .env*
10
+ .vscode
11
+ .idea
12
+ *.log
13
+ .DS_Store
@@ -0,0 +1,53 @@
1
+ name: Build and publish Docker image
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ tags: ['v*']
7
+
8
+ permissions:
9
+ contents: read
10
+ packages: write
11
+
12
+ jobs:
13
+ docker:
14
+ name: Build and push to ghcr.io and docker.io
15
+ runs-on: ubuntu-latest
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+
19
+ - uses: docker/setup-buildx-action@v3
20
+
21
+ - uses: docker/login-action@v3
22
+ with:
23
+ registry: ghcr.io
24
+ username: ${{ github.actor }}
25
+ password: ${{ secrets.GITHUB_TOKEN }}
26
+
27
+ - uses: docker/login-action@v3
28
+ with:
29
+ registry: docker.io
30
+ username: ${{ secrets.DOCKERHUB_USERNAME }}
31
+ password: ${{ secrets.DOCKERHUB_TOKEN }}
32
+
33
+ - uses: docker/metadata-action@v5
34
+ id: meta
35
+ with:
36
+ images: |
37
+ ghcr.io/glassmkr/crucible
38
+ docker.io/glassmkr/crucible
39
+ tags: |
40
+ type=semver,pattern={{version}}
41
+ type=semver,pattern={{major}}.{{minor}}
42
+ type=raw,value=latest,enable={{is_default_branch}}
43
+ type=sha,prefix=sha-,format=short
44
+
45
+ - uses: docker/build-push-action@v6
46
+ with:
47
+ context: .
48
+ platforms: linux/amd64
49
+ push: true
50
+ tags: ${{ steps.meta.outputs.tags }}
51
+ labels: ${{ steps.meta.outputs.labels }}
52
+ cache-from: type=gha
53
+ cache-to: type=gha,mode=max
package/Dockerfile ADDED
@@ -0,0 +1,59 @@
1
+ # syntax=docker/dockerfile:1.7
2
+ # Multi-stage build for Glassmkr Crucible monitoring agent.
3
+
4
+ # ---------- Stage 1: build TypeScript to dist/ ----------
5
+ FROM node:24-slim AS builder
6
+ WORKDIR /build
7
+ COPY package.json package-lock.json* ./
8
+ RUN npm install --include=dev --no-audit --no-fund
9
+ COPY tsconfig.json ./
10
+ COPY src ./src
11
+ RUN npm run build
12
+
13
+ # ---------- Stage 2: production runtime ----------
14
+ FROM node:24-slim AS runtime
15
+
16
+ # Hardware monitoring tools. Crucible shells out to these; they must be on PATH.
17
+ RUN apt-get update && apt-get install -y --no-install-recommends \
18
+ smartmontools \
19
+ ipmitool \
20
+ dmidecode \
21
+ lm-sensors \
22
+ ethtool \
23
+ util-linux \
24
+ procps \
25
+ net-tools \
26
+ iproute2 \
27
+ ca-certificates \
28
+ && rm -rf /var/lib/apt/lists/*
29
+
30
+ WORKDIR /app
31
+
32
+ # Production node_modules only.
33
+ COPY package.json package-lock.json* ./
34
+ RUN npm ci --omit=dev --no-audit --no-fund && npm cache clean --force
35
+
36
+ # Built code.
37
+ COPY --from=builder /build/dist ./dist
38
+
39
+ # Create a non-root user for future use. IPMI and SMART typically require root,
40
+ # so the container is expected to run with --privileged or cap_add DAC_READ_SEARCH etc.
41
+ # Keeping the user available lets operators drop privileges when hardware access is not needed.
42
+ RUN useradd --system --no-create-home --shell /usr/sbin/nologin glassmkr
43
+
44
+ # Crucible reads /etc/glassmkr/collector.yaml by default.
45
+ # Mount the host config directory at this path.
46
+ RUN mkdir -p /etc/glassmkr
47
+
48
+ # Container health: verify the Node process is actually running and hasn't crashed.
49
+ HEALTHCHECK --interval=60s --timeout=10s --start-period=30s --retries=3 \
50
+ CMD pgrep -f "node /app/dist/index.js" > /dev/null || exit 1
51
+
52
+ LABEL org.opencontainers.image.source="https://github.com/glassmkr/crucible" \
53
+ org.opencontainers.image.description="Glassmkr Crucible - bare metal server monitoring agent" \
54
+ org.opencontainers.image.licenses="MIT" \
55
+ org.opencontainers.image.title="Crucible" \
56
+ org.opencontainers.image.vendor="Glassmkr"
57
+
58
+ # Crucible does not listen on any port; data flows outbound to Forge.
59
+ ENTRYPOINT ["node", "/app/dist/index.js"]
package/README.md CHANGED
@@ -3,10 +3,25 @@
3
3
  [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
4
4
  [![npm version](https://img.shields.io/npm/v/@glassmkr/crucible.svg)](https://www.npmjs.com/package/@glassmkr/crucible)
5
5
 
6
- Lightweight bare metal server monitoring agent. Collects hardware and OS health every 5 minutes and pushes snapshots to a [Forge](https://forge.glassmkr.com) dashboard, which evaluates 36 alert rules and sends notifications.
6
+ Lightweight bare metal server monitoring agent. Collects hardware and OS health every 5 minutes and pushes snapshots to a [Forge](https://forge.glassmkr.com) dashboard, which evaluates 38 alert rules and sends notifications.
7
7
 
8
8
  Open source. MIT licensed. Built by [Glassmkr](https://glassmkr.com). See also [Bench](https://github.com/glassmkr/bench), the MCP server collection.
9
9
 
10
+ **Resource usage:** ~90MB RSS memory (varies by hardware: servers with more IPMI sensors use more), <0.1% CPU at 5-minute collection interval. Collects IPMI, SMART, ZFS, network bonds, security posture, conntrack, systemd, NTP, and file descriptors.
11
+
12
+ **Security:** See [glassmkr.com/security](https://glassmkr.com/security) for the full list of what Crucible does and does not collect.
13
+
14
+ ## Screenshots
15
+
16
+ ![Forge alerts with fix commands](https://glassmkr.com/screenshots/alerts.png)
17
+ *Alerts grouped by server, with AI-generated fix commands for each rule.*
18
+
19
+ ![Storage, SMART health, and network bonds](https://glassmkr.com/screenshots/hardware.png)
20
+ *Per-disk SMART status, storage capacity, and network interface bonding.*
21
+
22
+ ![Security posture and server overview](https://glassmkr.com/screenshots/overview.png)
23
+ *Security posture, server overview, and active alerts.*
24
+
10
25
  ## Install
11
26
 
12
27
  ```bash
@@ -19,6 +34,35 @@ Or use the bootstrap script:
19
34
  curl -sf https://forge.glassmkr.com/install | bash
20
35
  ```
21
36
 
37
+ ## Docker
38
+
39
+ ```bash
40
+ # Create config directory
41
+ sudo mkdir -p /etc/glassmkr
42
+
43
+ # Create config (replace with your Forge credentials)
44
+ sudo tee /etc/glassmkr/collector.yaml << 'EOF'
45
+ server_name: "web-01"
46
+ collection:
47
+ interval_seconds: 300
48
+ ipmi: true
49
+ smart: true
50
+ forge:
51
+ enabled: true
52
+ url: "https://forge.glassmkr.com"
53
+ api_key: "col_YOUR_KEY_HERE"
54
+ EOF
55
+
56
+ # Run with docker compose
57
+ curl -O https://raw.githubusercontent.com/glassmkr/crucible/main/docker-compose.yml
58
+ docker compose up -d
59
+
60
+ # Check logs
61
+ docker compose logs -f crucible
62
+ ```
63
+
64
+ Images are published to [ghcr.io/glassmkr/crucible](https://github.com/glassmkr/crucible/pkgs/container/crucible) on every tag release. The container needs `--privileged` and `network_mode: host` for IPMI, SMART, and accurate host network monitoring. Details in the [compose file](./docker-compose.yml).
65
+
22
66
  ## Quick Start
23
67
 
24
68
  1. Create an API key in the Forge dashboard (Servers, then Add server).
@@ -48,6 +92,8 @@ curl -sf https://forge.glassmkr.com/install | bash
48
92
 
49
93
  ```
50
94
  glassmkr-crucible [options]
95
+ glassmkr-crucible mark-reboot [--reason TEXT] [--ttl DURATION]
96
+ glassmkr-crucible reboot [--reason TEXT] [--ttl DURATION]
51
97
 
52
98
  Options:
53
99
  -v, --version Print version and exit
@@ -57,6 +103,29 @@ Options:
57
103
 
58
104
  `--config=PATH` and the legacy positional form `glassmkr-crucible /path/to.yaml` both work. Without options, Crucible runs as a long-lived collector daemon.
59
105
 
106
+ ## Rebooting without noise
107
+
108
+ Crucible distinguishes planned reboots from unplanned ones and gives each rule a short grace period after boot so that transient conditions (bond slave still negotiating, clock not synced yet) do not page you.
109
+
110
+ Before a planned reboot:
111
+
112
+ ```
113
+ sudo glassmkr-crucible reboot --reason "kernel update"
114
+ ```
115
+
116
+ Or, if you prefer to trigger the reboot yourself:
117
+
118
+ ```
119
+ sudo glassmkr-crucible mark-reboot --reason "kernel update"
120
+ sudo reboot
121
+ ```
122
+
123
+ Both write a short-lived marker to `/var/lib/crucible/reboot-expected`. The agent reads it once on startup, sets `expected_reboot: true` on the first post-boot snapshot, and deletes the file. Forge reads that flag and suppresses the `server_rebooted_unexpectedly` alert for that boot only.
124
+
125
+ The marker is single-use and expires 10 minutes after it is written (override with `--ttl 5m` / `--ttl 1h`), so a forgotten marker cannot silence a genuine crash reboot next week. If systemd fails to reboot the host, the marker simply expires on its own.
126
+
127
+ Per-rule grace windows are applied separately: bond-slave-down and CPU-temperature get 60 s, interface errors 120 s, clock-sync / NTP 300 s, others 0 s. Suppressed evaluations are recorded in alert history with status `suppressed_boot_grace` or `suppressed_planned_reboot` so you can audit exactly why a rule didn't fire during a given boot.
128
+
60
129
  ## Systemd Service
61
130
 
62
131
  Create `/etc/systemd/system/glassmkr-crucible.service`:
@@ -104,7 +173,7 @@ sudo systemctl status glassmkr-crucible
104
173
  | NTP | Sync state and source |
105
174
  | File descriptors | System-wide allocation |
106
175
 
107
- Forge evaluates 36 alert rules server-side across OS, Storage, Network, Hardware, ZFS, Security, and Service Health, with priorities P1 Urgent through P4 Low. Full list: [forge.glassmkr.com/docs/alerts](https://forge.glassmkr.com/docs/alerts).
176
+ Forge evaluates 38 alert rules server-side across OS, Storage, Network, Hardware, ZFS, Security, and Service Health, with priorities P1 Urgent through P4 Low. Full list: [forge.glassmkr.com/docs/alerts](https://forge.glassmkr.com/docs/alerts).
108
177
 
109
178
  ## Requirements
110
179
 
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,110 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
+ import { mkdtempSync, existsSync, writeFileSync, statSync, rmSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import { consumeRebootMarker, writeRebootMarker, parseDuration, } from "../lib/reboot-marker.js";
6
+ import { parseCliArgs } from "../cli.js";
7
+ let tmpDir;
8
+ let path;
9
+ beforeEach(() => {
10
+ tmpDir = mkdtempSync(join(tmpdir(), "crucible-test-"));
11
+ path = join(tmpDir, "reboot-expected");
12
+ });
13
+ afterEach(() => {
14
+ try {
15
+ rmSync(tmpDir, { recursive: true, force: true });
16
+ }
17
+ catch { }
18
+ });
19
+ describe("consumeRebootMarker", () => {
20
+ it("7. marker present, not expired: returns flag, deletes file", () => {
21
+ const future = new Date(Date.now() + 5 * 60_000).toISOString();
22
+ writeFileSync(path, JSON.stringify({ expires_at: future, reason: "kernel update" }));
23
+ const out = consumeRebootMarker(path);
24
+ expect(out).toEqual({ expected: true, reason: "kernel update" });
25
+ expect(existsSync(path)).toBe(false);
26
+ });
27
+ it("8. marker present, expired: returns null, deletes file", () => {
28
+ const past = new Date(Date.now() - 60_000).toISOString();
29
+ writeFileSync(path, JSON.stringify({ expires_at: past, reason: "stale" }));
30
+ expect(consumeRebootMarker(path)).toBeNull();
31
+ expect(existsSync(path)).toBe(false);
32
+ });
33
+ it("9. marker absent: returns null, no throw", () => {
34
+ expect(consumeRebootMarker(path)).toBeNull();
35
+ });
36
+ it("15. malformed JSON: returns null, file deleted, no crash", () => {
37
+ writeFileSync(path, "{not json at all");
38
+ expect(consumeRebootMarker(path)).toBeNull();
39
+ expect(existsSync(path)).toBe(false);
40
+ });
41
+ it("invalid expires_at (missing): returns null, file deleted", () => {
42
+ writeFileSync(path, JSON.stringify({ reason: "oops" }));
43
+ expect(consumeRebootMarker(path)).toBeNull();
44
+ expect(existsSync(path)).toBe(false);
45
+ });
46
+ it("consumed marker cannot be re-read (single-use)", () => {
47
+ const future = new Date(Date.now() + 60_000).toISOString();
48
+ writeFileSync(path, JSON.stringify({ expires_at: future }));
49
+ expect(consumeRebootMarker(path)).not.toBeNull();
50
+ expect(consumeRebootMarker(path)).toBeNull();
51
+ });
52
+ });
53
+ describe("writeRebootMarker", () => {
54
+ it("13. writes file at given path with correct TTL and reason, 0600 mode", () => {
55
+ const now = new Date("2026-04-21T22:00:00Z");
56
+ const res = writeRebootMarker({ path, reason: "kernel update", ttlMs: 10 * 60_000, now });
57
+ expect(res.path).toBe(path);
58
+ expect(res.expires_at).toBe("2026-04-21T22:10:00.000Z");
59
+ expect(existsSync(path)).toBe(true);
60
+ const mode = statSync(path).mode & 0o777;
61
+ expect(mode).toBe(0o600);
62
+ const round = consumeRebootMarker(path, new Date("2026-04-21T22:05:00Z"));
63
+ expect(round).toEqual({ expected: true, reason: "kernel update" });
64
+ });
65
+ it("default TTL is 10 minutes", () => {
66
+ const now = new Date("2026-04-21T22:00:00Z");
67
+ const res = writeRebootMarker({ path, now });
68
+ expect(res.expires_at).toBe("2026-04-21T22:10:00.000Z");
69
+ });
70
+ });
71
+ describe("parseDuration", () => {
72
+ it.each([
73
+ ["10m", 600_000],
74
+ ["2h", 7_200_000],
75
+ ["600s", 600_000],
76
+ ["500ms", 500],
77
+ ["30", 30_000], // bare number -> seconds
78
+ ])("%s -> %d ms", (input, ms) => {
79
+ expect(parseDuration(input)).toBe(ms);
80
+ });
81
+ it("rejects garbage", () => {
82
+ expect(parseDuration("forever")).toBeNull();
83
+ expect(parseDuration("-5m")).toBeNull();
84
+ expect(parseDuration("")).toBeNull();
85
+ });
86
+ });
87
+ describe("CLI parseCliArgs subcommands", () => {
88
+ it("14. `reboot` subcommand captured with flags", () => {
89
+ const { result } = parseCliArgs(["reboot", "--reason", "kernel update"], "1.0.0");
90
+ expect(result.mode).toBe("reboot");
91
+ expect(result.reason).toBe("kernel update");
92
+ });
93
+ it("`mark-reboot` with --ttl parsed through", () => {
94
+ const { result } = parseCliArgs(["mark-reboot", "--ttl=5m", "--reason=test"], "1.0.0");
95
+ expect(result.mode).toBe("mark-reboot");
96
+ expect(result.ttl).toBe("5m");
97
+ expect(result.reason).toBe("test");
98
+ });
99
+ it("`mark-reboot --help` returns help output without running", () => {
100
+ const { result, output } = parseCliArgs(["mark-reboot", "--help"], "1.0.0");
101
+ expect(result.mode).toBe("help");
102
+ expect(output).toContain("mark-reboot");
103
+ });
104
+ it("top-level help lists the new subcommands", () => {
105
+ const { output } = parseCliArgs(["--help"], "1.0.0");
106
+ expect(output).toMatch(/mark-reboot/);
107
+ expect(output).toMatch(/reboot/);
108
+ });
109
+ });
110
+ //# sourceMappingURL=reboot-marker.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reboot-marker.test.js","sourceRoot":"","sources":["../../src/__tests__/reboot-marker.test.ts"],"names":[],"mappings":"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"}
package/dist/cli.d.ts CHANGED
@@ -1,6 +1,9 @@
1
+ export type CliMode = "version" | "help" | "run" | "mark-reboot" | "reboot";
1
2
  export interface CliArgs {
2
- mode: "version" | "help" | "run";
3
+ mode: CliMode;
3
4
  configPath: string;
5
+ reason?: string;
6
+ ttl?: string;
4
7
  }
5
8
  export declare const DEFAULT_CONFIG_PATH = "/etc/glassmkr/collector.yaml";
6
9
  export declare function parseCliArgs(argv: string[], version: string): {
package/dist/cli.js CHANGED
@@ -5,6 +5,36 @@ export const DEFAULT_CONFIG_PATH = "/etc/glassmkr/collector.yaml";
5
5
  export function parseCliArgs(argv, version) {
6
6
  // argv is typically process.argv.slice(2)
7
7
  let configPath = DEFAULT_CONFIG_PATH;
8
+ // Subcommand dispatch: `mark-reboot` and `reboot` take their own flags
9
+ // (--reason, --ttl) but re-use --help.
10
+ if (argv[0] === "mark-reboot" || argv[0] === "reboot") {
11
+ const mode = argv[0];
12
+ let reason;
13
+ let ttl;
14
+ for (let i = 1; i < argv.length; i++) {
15
+ const a = argv[i];
16
+ if (a === "--help" || a === "-h") {
17
+ return { result: { mode: "help", configPath: "" }, output: subcommandHelp(mode, version) };
18
+ }
19
+ if (a === "--reason") {
20
+ reason = argv[++i];
21
+ continue;
22
+ }
23
+ if (a.startsWith("--reason=")) {
24
+ reason = a.slice("--reason=".length);
25
+ continue;
26
+ }
27
+ if (a === "--ttl") {
28
+ ttl = argv[++i];
29
+ continue;
30
+ }
31
+ if (a.startsWith("--ttl=")) {
32
+ ttl = a.slice("--ttl=".length);
33
+ continue;
34
+ }
35
+ }
36
+ return { result: { mode, configPath: "", reason, ttl }, output: null };
37
+ }
8
38
  for (let i = 0; i < argv.length; i++) {
9
39
  const arg = argv[i];
10
40
  if (arg === "--version" || arg === "-v") {
@@ -40,14 +70,40 @@ export function helpText(version) {
40
70
  "",
41
71
  "Usage:",
42
72
  " glassmkr-crucible [options]",
73
+ " glassmkr-crucible mark-reboot [--reason TEXT] [--ttl DURATION]",
74
+ " glassmkr-crucible reboot [--reason TEXT] [--ttl DURATION]",
43
75
  "",
44
76
  "Options:",
45
77
  " -v, --version Print version and exit",
46
78
  " -h, --help Print this help and exit",
47
79
  ` -c, --config Path to config file (default: ${DEFAULT_CONFIG_PATH})`,
48
80
  "",
81
+ "Subcommands:",
82
+ " mark-reboot Write a planned-reboot marker so the next boot",
83
+ " does not fire `server_rebooted_unexpectedly`.",
84
+ " You run the reboot yourself afterwards.",
85
+ " reboot Write the marker, then invoke `systemctl reboot`.",
86
+ "",
49
87
  "Without options, starts the collector daemon using the config file.",
50
88
  "Docs: https://github.com/glassmkr/crucible",
51
89
  ].join("\n");
52
90
  }
91
+ function subcommandHelp(mode, version) {
92
+ const action = mode === "reboot"
93
+ ? "Write a planned-reboot marker and invoke `systemctl reboot`."
94
+ : "Write a planned-reboot marker; operator triggers the reboot.";
95
+ return [
96
+ `glassmkr-crucible ${mode} - ${action}`,
97
+ "",
98
+ "Usage:",
99
+ ` glassmkr-crucible ${mode} [--reason TEXT] [--ttl DURATION]`,
100
+ "",
101
+ "Options:",
102
+ ' --reason TEXT Free-text reason (e.g. "kernel update")',
103
+ " --ttl DURATION Expiry window; e.g. 5m, 10m, 1h (default 10m)",
104
+ "",
105
+ `Marker path: /var/lib/crucible/reboot-expected (requires root).`,
106
+ `v${version}`,
107
+ ].join("\n");
108
+ }
53
109
  //# sourceMappingURL=cli.js.map
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,6EAA6E;AAC7E,8EAA8E;AAO9E,MAAM,CAAC,MAAM,mBAAmB,GAAG,8BAA8B,CAAC;AAElE,MAAM,UAAU,YAAY,CAAC,IAAc,EAAE,OAAe;IAC1D,0CAA0C;IAC1C,IAAI,UAAU,GAAG,mBAAmB,CAAC;IAErC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QACpB,IAAI,GAAG,KAAK,WAAW,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;YACxC,OAAO,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,UAAU,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,sBAAsB,OAAO,EAAE,EAAE,CAAC;QAClG,CAAC;QACD,IAAI,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;YACrC,OAAO,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QACjF,CAAC;QACD,+BAA+B;QAC/B,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,UAAU,EAAE,CAAC;YACvC,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YACzB,IAAI,IAAI,EAAE,CAAC;gBACT,UAAU,GAAG,IAAI,CAAC;gBAClB,CAAC,EAAE,CAAC;YACN,CAAC;YACD,SAAS;QACX,CAAC;QACD,kBAAkB;QAClB,IAAI,GAAG,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;YAChC,UAAU,GAAG,GAAG,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;YAC3C,SAAS;QACX,CAAC;QACD,mDAAmD;QACnD,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACzB,UAAU,GAAG,GAAG,CAAC;QACnB,CAAC;IACH,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;AAC/D,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,OAAe;IACtC,OAAO;QACL,sBAAsB,OAAO,uCAAuC;QACpE,EAAE;QACF,QAAQ;QACR,+BAA+B;QAC/B,EAAE;QACF,UAAU;QACV,2CAA2C;QAC3C,6CAA6C;QAC7C,oDAAoD,mBAAmB,GAAG;QAC1E,EAAE;QACF,qEAAqE;QACrE,4CAA4C;KAC7C,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,25 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { readOsReleaseField } from "../system.js";
3
+ describe("readOsReleaseField", () => {
4
+ it("parses unquoted Ubuntu values", () => {
5
+ const s = 'NAME="Ubuntu"\nID=ubuntu\nID_LIKE=debian\nVERSION_ID="24.04"';
6
+ expect(readOsReleaseField(s, "ID")).toBe("ubuntu");
7
+ expect(readOsReleaseField(s, "ID_LIKE")).toBe("debian");
8
+ });
9
+ it("parses quoted RHEL-family values", () => {
10
+ const s = 'NAME="Rocky Linux"\nID="rocky"\nID_LIKE="rhel centos fedora"';
11
+ expect(readOsReleaseField(s, "ID")).toBe("rocky");
12
+ expect(readOsReleaseField(s, "ID_LIKE")).toBe("rhel centos fedora");
13
+ });
14
+ it("lowercases the result (some distros uppercase their ID)", () => {
15
+ expect(readOsReleaseField("ID=Alpine", "ID")).toBe("alpine");
16
+ });
17
+ it("returns undefined for a missing key", () => {
18
+ expect(readOsReleaseField("ID=arch", "ID_LIKE")).toBeUndefined();
19
+ });
20
+ it("does not confuse ID with VERSION_ID", () => {
21
+ const s = 'VERSION_ID="24.04"\nID=ubuntu';
22
+ expect(readOsReleaseField(s, "ID")).toBe("ubuntu");
23
+ });
24
+ });
25
+ //# sourceMappingURL=system.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"system.test.js","sourceRoot":"","sources":["../../../src/collect/__tests__/system.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAElD,QAAQ,CAAC,oBAAoB,EAAE,GAAG,EAAE;IAClC,EAAE,CAAC,+BAA+B,EAAE,GAAG,EAAE;QACvC,MAAM,CAAC,GAAG,8DAA8D,CAAC;QACzE,MAAM,CAAC,kBAAkB,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACnD,MAAM,CAAC,kBAAkB,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC1D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;QAC1C,MAAM,CAAC,GAAG,8DAA8D,CAAC;QACzE,MAAM,CAAC,kBAAkB,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAClD,MAAM,CAAC,kBAAkB,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;IACtE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yDAAyD,EAAE,GAAG,EAAE;QACjE,MAAM,CAAC,kBAAkB,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC/D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qCAAqC,EAAE,GAAG,EAAE;QAC7C,MAAM,CAAC,kBAAkB,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IACnE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qCAAqC,EAAE,GAAG,EAAE;QAC7C,MAAM,CAAC,GAAG,+BAA+B,CAAC;QAC1C,MAAM,CAAC,kBAAkB,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -1,6 +1,16 @@
1
1
  import { readProcFile, sleep } from "../lib/parse.js";
2
2
  import { readFileSync, readdirSync } from "fs";
3
3
  const previousCounters = new Map();
4
+ function readStatCounter(iface, name) {
5
+ try {
6
+ const raw = readFileSync(`/sys/class/net/${iface}/statistics/${name}`, "utf-8").trim();
7
+ const val = parseInt(raw, 10);
8
+ return Number.isFinite(val) ? val : undefined;
9
+ }
10
+ catch {
11
+ return undefined;
12
+ }
13
+ }
4
14
  function parseNetDev() {
5
15
  const raw = readProcFile("/proc/net/dev") || "";
6
16
  const result = {};
@@ -52,6 +62,14 @@ function getBondMaster(iface) {
52
62
  }
53
63
  return undefined;
54
64
  }
65
+ function isBondMaster(iface) {
66
+ try {
67
+ return readdirSync("/proc/net/bonding/").includes(iface);
68
+ }
69
+ catch {
70
+ return false;
71
+ }
72
+ }
55
73
  // Compute delta, handling counter wraps (current < previous means reset, use current as delta)
56
74
  function delta(current, previous) {
57
75
  if (current >= previous)
@@ -70,16 +88,39 @@ export async function collectNetwork() {
70
88
  continue;
71
89
  currentIfaces.add(name);
72
90
  const prev = previousCounters.get(name);
91
+ // /sys/class/net/*/statistics/ exposes finer-grained RX/TX subtype
92
+ // counters than /proc/net/dev. Read cumulative values here; delta is
93
+ // derived below against the previous cycle's snapshot.
94
+ const rxCrcCum = readStatCounter(name, "rx_crc_errors");
95
+ const rxFrameCum = readStatCounter(name, "rx_frame_errors");
96
+ const rxLenCum = readStatCounter(name, "rx_length_errors");
97
+ const txCarrierCum = readStatCounter(name, "tx_carrier_errors");
73
98
  // Compute error/drop deltas (0 on first cycle after start or new interface)
74
99
  let rxErrorsDelta = 0;
75
100
  let txErrorsDelta = 0;
76
101
  let rxDropsDelta = 0;
77
102
  let txDropsDelta = 0;
103
+ let rxPacketsDelta = 0;
104
+ let txPacketsDelta = 0;
105
+ let rxCrcDelta;
106
+ let rxFrameDelta;
107
+ let rxLenDelta;
108
+ let txCarrierDelta;
78
109
  if (prev) {
79
110
  rxErrorsDelta = delta(s2.rx_errors, prev.rx_errors);
80
111
  txErrorsDelta = delta(s2.tx_errors, prev.tx_errors);
81
112
  rxDropsDelta = delta(s2.rx_drops, prev.rx_drops);
82
113
  txDropsDelta = delta(s2.tx_drops, prev.tx_drops);
114
+ rxPacketsDelta = delta(s2.rx_packets, prev.rx_packets);
115
+ txPacketsDelta = delta(s2.tx_packets, prev.tx_packets);
116
+ if (rxCrcCum != null && prev.rx_crc_errors != null)
117
+ rxCrcDelta = delta(rxCrcCum, prev.rx_crc_errors);
118
+ if (rxFrameCum != null && prev.rx_frame_errors != null)
119
+ rxFrameDelta = delta(rxFrameCum, prev.rx_frame_errors);
120
+ if (rxLenCum != null && prev.rx_length_errors != null)
121
+ rxLenDelta = delta(rxLenCum, prev.rx_length_errors);
122
+ if (txCarrierCum != null && prev.tx_carrier_errors != null)
123
+ txCarrierDelta = delta(txCarrierCum, prev.tx_carrier_errors);
83
124
  }
84
125
  // Store current cumulative values for next cycle
85
126
  previousCounters.set(name, {
@@ -87,6 +128,12 @@ export async function collectNetwork() {
87
128
  tx_errors: s2.tx_errors,
88
129
  rx_drops: s2.rx_drops,
89
130
  tx_drops: s2.tx_drops,
131
+ rx_packets: s2.rx_packets,
132
+ tx_packets: s2.tx_packets,
133
+ rx_crc_errors: rxCrcCum,
134
+ rx_frame_errors: rxFrameCum,
135
+ rx_length_errors: rxLenCum,
136
+ tx_carrier_errors: txCarrierCum,
90
137
  });
91
138
  const entry = {
92
139
  interface: name,
@@ -97,11 +144,24 @@ export async function collectNetwork() {
97
144
  tx_errors: txErrorsDelta,
98
145
  rx_drops: rxDropsDelta,
99
146
  tx_drops: txDropsDelta,
147
+ rx_packets: rxPacketsDelta,
148
+ tx_packets: txPacketsDelta,
100
149
  operstate: getOperstate(name),
101
150
  };
151
+ if (rxCrcDelta !== undefined)
152
+ entry.rx_crc_errors = rxCrcDelta;
153
+ if (rxFrameDelta !== undefined)
154
+ entry.rx_frame_errors = rxFrameDelta;
155
+ if (rxLenDelta !== undefined)
156
+ entry.rx_length_errors = rxLenDelta;
157
+ if (txCarrierDelta !== undefined)
158
+ entry.tx_carrier_errors = txCarrierDelta;
102
159
  const master = getBondMaster(name);
103
160
  if (master)
104
161
  entry.bond_master = master;
162
+ // Identify bond masters (have at least one slave pointing at them).
163
+ if (isBondMaster(name))
164
+ entry.is_bond_master = true;
105
165
  results.push(entry);
106
166
  }
107
167
  // Remove stale interfaces that disappeared
@@ -1 +1 @@
1
- {"version":3,"file":"network.js","sourceRoot":"","sources":["../../src/collect/network.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"network.js","sourceRoot":"","sources":["../../src/collect/network.ts"],"names":[],"mappings":"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"}
@@ -1,2 +1,3 @@
1
1
  import type { SystemInfo } from "../lib/types.js";
2
+ export declare function readOsReleaseField(osRelease: string, key: string): string | undefined;
2
3
  export declare function collectSystem(): Promise<SystemInfo>;