@daemonitor/plugins 1.1.1 → 1.1.3

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.
@@ -7,6 +7,7 @@ import { createWebsitePlugin } from "./monitoring/WebsitePlugin";
7
7
  import { createWordpressPlugin } from "./monitoring/WordpressPlugin";
8
8
  import { createLogWatchPlugin } from "./monitoring/LogWatchPlugin";
9
9
  import { createNetScanPlugin } from "./monitoring/NetScanPlugin";
10
+ import { createSnmpPlugin } from "./monitoring/SnmpPlugin";
10
11
  // In future, implement other plugin factory functions using the same pattern
11
12
  // import { createOSPlugin } from "./monitoring/OSPlugin"
12
13
  // import { createMongoDBPlugin } from "./monitoring/MongoDBPlugin"
@@ -25,6 +26,7 @@ export default {
25
26
  Wordpress: createWordpressPlugin,
26
27
  Logwatch: createLogWatchPlugin,
27
28
  Netscan: createNetScanPlugin,
29
+ Snmp: createSnmpPlugin,
28
30
  // CloudflarePlugin: createCloudflarePlugin,
29
31
  // EwelinkPlugin: createEwelinkPlugin,
30
32
  // MongoDBPlugin: createMongoDBPlugin,
@@ -0,0 +1,119 @@
1
+ import { execFile } from "node:child_process";
2
+ import { promisify } from "node:util";
3
+ import { createMonitoringPlugin } from "../../lib/MonitoringPlugin";
4
+ // SNMP monitor. First target class: printers (standard Printer-MIB) — model,
5
+ // serial, lifetime page count, and per-supply toner/consumable levels. Shells
6
+ // out to net-snmp's snmpget/snmpwalk (add `snmp` to the client image) rather
7
+ // than pulling an npm SNMP stack, matching the other CLI-backed plugins.
8
+ //
9
+ // One client_state per target (type "snmp-printer", uid snmp-<slug>). Severity
10
+ // is decided server-side (clientstate/update.put.ts): a supply at/under its
11
+ // warn threshold → warning; an error/offline printer status → error.
12
+ const pexec = promisify(execFile);
13
+ // Printer-MIB / HOST-RESOURCES OIDs
14
+ const OID = {
15
+ model: "1.3.6.1.2.1.25.3.2.1.3.1", // hrDeviceDescr
16
+ serial: "1.3.6.1.2.1.43.5.1.1.17.1", // prtGeneralSerialNumber
17
+ pages: "1.3.6.1.2.1.43.10.2.1.4.1.1", // prtMarkerLifeCount
18
+ status: "1.3.6.1.2.1.25.3.5.1.1.1", // hrPrinterStatus (3=idle,4=printing,5=warmup)
19
+ supplyDesc: "1.3.6.1.2.1.43.11.1.1.6", // prtMarkerSuppliesDescription (walk)
20
+ supplyMax: "1.3.6.1.2.1.43.11.1.1.8", // prtMarkerSuppliesMaxCapacity (walk)
21
+ supplyLevel: "1.3.6.1.2.1.43.11.1.1.9", // prtMarkerSuppliesLevel (walk)
22
+ };
23
+ const PRINTER_STATUS = { "1": "other", "2": "unknown", "3": "idle", "4": "printing", "5": "warmup" };
24
+ function slug(s) {
25
+ return s.replace(/[^a-zA-Z0-9_.-]+/g, "-").replace(/^-+|-+$/g, "");
26
+ }
27
+ async function snmpGet(t, oid, timeout = 8000) {
28
+ try {
29
+ const { stdout } = await pexec("snmpget", ["-v", t.version || "2c", "-c", t.community || "public", "-Ovq", "-t", "2", "-r", "1", t.ip, oid], { timeout });
30
+ const v = stdout.trim().replace(/^"|"$/g, "");
31
+ return v && !/No Such|no response|Timeout/i.test(v) ? v : null;
32
+ }
33
+ catch {
34
+ return null;
35
+ }
36
+ }
37
+ async function snmpWalk(t, oid, timeout = 10000) {
38
+ try {
39
+ const { stdout } = await pexec("snmpwalk", ["-v", t.version || "2c", "-c", t.community || "public", "-Oqv", "-t", "2", "-r", "1", t.ip, oid], { timeout });
40
+ return stdout.split("\n").map((l) => l.trim().replace(/^"|"$/g, "")).filter(Boolean);
41
+ }
42
+ catch {
43
+ return [];
44
+ }
45
+ }
46
+ async function collectPrinter(t) {
47
+ const warnPct = t.tonerWarnPct ?? 10;
48
+ const [model, serial, pagesRaw, statusRaw, descs, maxes, levels] = await Promise.all([
49
+ snmpGet(t, OID.model),
50
+ snmpGet(t, OID.serial),
51
+ snmpGet(t, OID.pages),
52
+ snmpGet(t, OID.status),
53
+ snmpWalk(t, OID.supplyDesc),
54
+ snmpWalk(t, OID.supplyMax),
55
+ snmpWalk(t, OID.supplyLevel),
56
+ ]);
57
+ // No response at all → the printer is unreachable / SNMP off.
58
+ if (model == null && serial == null && pagesRaw == null && !descs.length) {
59
+ return { name: t.name, ip: t.ip, kind: "printer", reachable: false };
60
+ }
61
+ // Pair descriptions with levels. prtMarkerSuppliesLevel semantics: -2 =
62
+ // unknown, -3 = "some remaining" (a level the device won't quantify); else a
63
+ // count out of MaxCapacity (which can itself be -2 = unknown).
64
+ const supplies = descs.map((desc, i) => {
65
+ const max = Number(maxes[i]);
66
+ const lvl = Number(levels[i]);
67
+ let pct = null;
68
+ if (lvl === -3)
69
+ pct = 100; // "ok / some remaining"
70
+ else if (lvl >= 0 && max > 0)
71
+ pct = Math.round((lvl / max) * 100);
72
+ return { name: desc, level: lvl, max, pct };
73
+ });
74
+ const low = supplies.filter((s) => s.pct != null && s.pct <= warnPct);
75
+ return {
76
+ name: t.name,
77
+ ip: t.ip,
78
+ kind: "printer",
79
+ reachable: true,
80
+ model: model || undefined,
81
+ serial: serial || undefined,
82
+ pages: pagesRaw != null ? Number(pagesRaw) : undefined,
83
+ status: statusRaw != null ? PRINTER_STATUS[statusRaw] || statusRaw : undefined,
84
+ supplies,
85
+ lowSupplies: low.map((s) => ({ name: s.name, pct: s.pct })),
86
+ warnPct,
87
+ };
88
+ }
89
+ export function createSnmpPlugin() {
90
+ let refreshTimer = null;
91
+ const targetsOf = (plugin) => {
92
+ const cfg = (plugin.config || {});
93
+ return (Array.isArray(cfg.targets) ? cfg.targets : [])
94
+ .filter((t) => t?.ip)
95
+ .map((t) => ({ ...t, community: t.community || cfg.community || "public" }));
96
+ };
97
+ const refreshFn = async (plugin) => {
98
+ await Promise.all(targetsOf(plugin).map(async (t) => {
99
+ try {
100
+ const data = t.kind === "printer" || !t.kind ? await collectPrinter(t) : { name: t.name, ip: t.ip, kind: t.kind };
101
+ await plugin.send(data, `snmp-${slug(t.name || t.ip)}`);
102
+ }
103
+ catch (e) {
104
+ await plugin.send({ name: t.name, ip: t.ip, kind: t.kind || "printer", error: String(e?.message || e).slice(0, 160) }, `snmp-${slug(t.name || t.ip)}`);
105
+ }
106
+ }));
107
+ };
108
+ const monitorFn = async (plugin) => {
109
+ await refreshFn(plugin);
110
+ refreshTimer = setInterval(() => refreshFn(plugin), plugin.config?.refreshInterval || 300000);
111
+ };
112
+ const teardownFn = async () => {
113
+ if (refreshTimer)
114
+ clearInterval(refreshTimer);
115
+ refreshTimer = null;
116
+ };
117
+ return createMonitoringPlugin("snmp", "snmp", "SNMP monitor (printers: model, pages, toner levels)", async () => { }, monitorFn, refreshFn, teardownFn);
118
+ }
119
+ export default createSnmpPlugin;
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@daemonitor/plugins",
3
- "version": "1.1.1",
3
+ "version": "1.1.3",
4
4
  "description": "daemonitor plugins",
5
5
  "types": "types/index.d.ts",
6
- "main": "index.ts",
6
+ "main": "dist/index.js",
7
7
  "type": "module",
8
8
  "scripts": {
9
9
  "build": "tsc",
@@ -15,22 +15,15 @@
15
15
  "types"
16
16
  ],
17
17
  "exports": {
18
- ".": "./index.ts",
19
- "./lib/*": "./lib/*.ts",
20
- "./plugins/*": "./plugins/*.ts"
18
+ ".": {
19
+ "types": "./types/index.d.ts",
20
+ "default": "./dist/index.js"
21
+ },
22
+ "./lib/*": "./dist/lib/*.js",
23
+ "./plugins/*": "./dist/plugins/*.js"
21
24
  },
22
25
  "publishConfig": {
23
- "access": "public",
24
- "main": "dist/index.js",
25
- "types": "types/index.d.ts",
26
- "exports": {
27
- ".": {
28
- "types": "./types/index.d.ts",
29
- "default": "./dist/index.js"
30
- },
31
- "./lib/*": "./dist/lib/*.js",
32
- "./plugins/*": "./dist/plugins/*.js"
33
- }
26
+ "access": "public"
34
27
  },
35
28
  "author": "",
36
29
  "license": "ISC",
package/index.ts DELETED
@@ -1,11 +0,0 @@
1
- // Export core plugin utilities
2
- export { PluginConfigProvider } from "./lib/PluginConfigProvider"
3
- export { PluginManager } from "./lib/PluginManager"
4
- export { PluginLoader } from "./lib/PluginLoader"
5
-
6
- // Only export creation functions, not types
7
- export { createBasePlugin } from "./lib/BasePlugin"
8
- export { createMonitoringPlugin } from "./lib/MonitoringPlugin"
9
-
10
- // Export the PM2 plugin factory function
11
- export { createPM2Plugin } from "./plugins/monitoring/PM2Plugin"