@camstack/addon-metrics-glances 0.1.1
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/chunk-WLTPZ3KJ.mjs +394 -0
- package/dist/chunk-WLTPZ3KJ.mjs.map +1 -0
- package/dist/glances-metrics.addon.d.mts +29 -0
- package/dist/glances-metrics.addon.d.ts +29 -0
- package/dist/glances-metrics.addon.js +412 -0
- package/dist/glances-metrics.addon.js.map +1 -0
- package/dist/glances-metrics.addon.mjs +7 -0
- package/dist/glances-metrics.addon.mjs.map +1 -0
- package/dist/index.d.mts +138 -0
- package/dist/index.d.ts +138 -0
- package/dist/index.js +422 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +11 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +69 -0
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/glances-metrics.addon.ts
|
|
21
|
+
var glances_metrics_addon_exports = {};
|
|
22
|
+
__export(glances_metrics_addon_exports, {
|
|
23
|
+
default: () => GlancesMetricsAddon
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(glances_metrics_addon_exports);
|
|
26
|
+
var import_types = require("@camstack/types");
|
|
27
|
+
|
|
28
|
+
// src/glances-client.ts
|
|
29
|
+
var GlancesClient = class {
|
|
30
|
+
constructor(baseUrl, timeoutMs = 3e3) {
|
|
31
|
+
this.baseUrl = baseUrl;
|
|
32
|
+
this.timeoutMs = timeoutMs;
|
|
33
|
+
}
|
|
34
|
+
baseUrl;
|
|
35
|
+
timeoutMs;
|
|
36
|
+
/** Check if Glances is reachable. */
|
|
37
|
+
async isReachable() {
|
|
38
|
+
try {
|
|
39
|
+
const res = await this.fetch("/status");
|
|
40
|
+
return res !== null;
|
|
41
|
+
} catch {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
async getCpu() {
|
|
46
|
+
return this.fetch("/cpu");
|
|
47
|
+
}
|
|
48
|
+
async getMem() {
|
|
49
|
+
return this.fetch("/mem");
|
|
50
|
+
}
|
|
51
|
+
async getDiskIo() {
|
|
52
|
+
return this.fetch("/diskio");
|
|
53
|
+
}
|
|
54
|
+
async getFs() {
|
|
55
|
+
return this.fetch("/fs");
|
|
56
|
+
}
|
|
57
|
+
async getNetwork() {
|
|
58
|
+
return this.fetch("/network");
|
|
59
|
+
}
|
|
60
|
+
async getSensors() {
|
|
61
|
+
return this.fetch("/sensors");
|
|
62
|
+
}
|
|
63
|
+
async getGpu() {
|
|
64
|
+
return this.fetch("/gpu");
|
|
65
|
+
}
|
|
66
|
+
async getLoad() {
|
|
67
|
+
return this.fetch("/load");
|
|
68
|
+
}
|
|
69
|
+
// ─── Internal ─────────────────────────────────────────────────────
|
|
70
|
+
async fetch(endpoint) {
|
|
71
|
+
try {
|
|
72
|
+
const url = `${this.baseUrl}/api/4${endpoint}`;
|
|
73
|
+
const res = await globalThis.fetch(url, {
|
|
74
|
+
signal: AbortSignal.timeout(this.timeoutMs),
|
|
75
|
+
headers: { "Accept": "application/json" }
|
|
76
|
+
});
|
|
77
|
+
if (!res.ok) return null;
|
|
78
|
+
return await res.json();
|
|
79
|
+
} catch {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
// src/glances-metrics-provider.ts
|
|
86
|
+
var GlancesMetricsProvider = class {
|
|
87
|
+
constructor(client, logger) {
|
|
88
|
+
this.client = client;
|
|
89
|
+
this.logger = logger;
|
|
90
|
+
}
|
|
91
|
+
client;
|
|
92
|
+
logger;
|
|
93
|
+
id = "glances";
|
|
94
|
+
cachedSnapshot = null;
|
|
95
|
+
samplingTimer = null;
|
|
96
|
+
// ─── Lifecycle (addon-owned, not on the cap contract) ───────────────
|
|
97
|
+
startSampling(intervalMs) {
|
|
98
|
+
this.collectSnapshot().then((s) => {
|
|
99
|
+
this.cachedSnapshot = s;
|
|
100
|
+
}).catch((err) => {
|
|
101
|
+
this.logger.warn("Initial Glances snapshot failed", { meta: { error: err instanceof Error ? err.message : String(err) } });
|
|
102
|
+
});
|
|
103
|
+
this.samplingTimer = setInterval(() => {
|
|
104
|
+
this.collectSnapshot().then((s) => {
|
|
105
|
+
this.cachedSnapshot = s;
|
|
106
|
+
}).catch(() => {
|
|
107
|
+
});
|
|
108
|
+
}, intervalMs);
|
|
109
|
+
if (this.samplingTimer.unref) this.samplingTimer.unref();
|
|
110
|
+
}
|
|
111
|
+
stopSampling() {
|
|
112
|
+
if (this.samplingTimer) {
|
|
113
|
+
clearInterval(this.samplingTimer);
|
|
114
|
+
this.samplingTimer = null;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
// ─── Cap contract ───────────────────────────────────────────────────
|
|
118
|
+
async collectSnapshot() {
|
|
119
|
+
const snapshot = await this.collectFromGlances();
|
|
120
|
+
this.cachedSnapshot = snapshot;
|
|
121
|
+
return snapshot;
|
|
122
|
+
}
|
|
123
|
+
async getCached() {
|
|
124
|
+
return this.cachedSnapshot;
|
|
125
|
+
}
|
|
126
|
+
async getCurrent() {
|
|
127
|
+
const s = this.cachedSnapshot ?? await this.collectSnapshot();
|
|
128
|
+
const mb = (bytes) => Math.round(bytes / 1024 / 1024);
|
|
129
|
+
return {
|
|
130
|
+
cpuPercent: s.cpu.total,
|
|
131
|
+
memoryPercent: s.memory.percent,
|
|
132
|
+
memoryUsedMB: mb(s.memory.usedBytes),
|
|
133
|
+
memoryTotalMB: mb(s.memory.totalBytes),
|
|
134
|
+
temperature: s.cpuTemperature ?? void 0,
|
|
135
|
+
gpuPercent: s.gpu?.utilization,
|
|
136
|
+
gpuMemoryPercent: s.gpu && s.gpu.memoryTotalBytes > 0 ? Math.round(s.gpu.memoryUsedBytes / s.gpu.memoryTotalBytes * 1e3) / 10 : void 0
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
async getDiskSpace(input) {
|
|
140
|
+
const fsData = await this.client.getFs();
|
|
141
|
+
if (!fsData) throw new Error("Glances /fs endpoint returned no data");
|
|
142
|
+
const match = fsData.find((f) => input.dirPath.startsWith(f.mnt_point));
|
|
143
|
+
if (!match) throw new Error("Glances reported no filesystem matching path: " + input.dirPath);
|
|
144
|
+
return {
|
|
145
|
+
path: match.mnt_point,
|
|
146
|
+
totalBytes: match.size,
|
|
147
|
+
usedBytes: match.used,
|
|
148
|
+
availableBytes: match.free,
|
|
149
|
+
percent: match.percent
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
async getGpuInfo() {
|
|
153
|
+
const gpuData = await this.client.getGpu();
|
|
154
|
+
if (!gpuData || gpuData.length === 0) return null;
|
|
155
|
+
const gpu = gpuData[0];
|
|
156
|
+
return {
|
|
157
|
+
utilization: gpu.proc,
|
|
158
|
+
model: gpu.name,
|
|
159
|
+
memoryUsedBytes: 0,
|
|
160
|
+
memoryTotalBytes: 0,
|
|
161
|
+
temperature: gpu.temperature ?? null
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
async getCpuTemperature() {
|
|
165
|
+
const sensors = await this.client.getSensors();
|
|
166
|
+
if (!sensors) return null;
|
|
167
|
+
const cpuTemp = sensors.find(
|
|
168
|
+
(s) => s.type === "temperature_core" || s.label.toLowerCase().includes("cpu")
|
|
169
|
+
);
|
|
170
|
+
return cpuTemp ? Math.round(cpuTemp.value * 10) / 10 : null;
|
|
171
|
+
}
|
|
172
|
+
async getProcessStats(_input) {
|
|
173
|
+
return [];
|
|
174
|
+
}
|
|
175
|
+
// ─── Internal ───────────────────────────────────────────────────────
|
|
176
|
+
async collectFromGlances() {
|
|
177
|
+
const [cpuData, memData, diskIoData, networkData, loadData, sensorsData, gpuData] = await Promise.all([
|
|
178
|
+
this.client.getCpu(),
|
|
179
|
+
this.client.getMem(),
|
|
180
|
+
this.client.getDiskIo(),
|
|
181
|
+
this.client.getNetwork(),
|
|
182
|
+
this.client.getLoad(),
|
|
183
|
+
this.client.getSensors(),
|
|
184
|
+
this.client.getGpu()
|
|
185
|
+
]);
|
|
186
|
+
if (!cpuData || !memData) {
|
|
187
|
+
throw new Error("Glances: core endpoints (cpu/mem) returned null");
|
|
188
|
+
}
|
|
189
|
+
const cpu = {
|
|
190
|
+
total: cpuData.total,
|
|
191
|
+
user: cpuData.user,
|
|
192
|
+
system: cpuData.system,
|
|
193
|
+
irq: cpuData.irq,
|
|
194
|
+
nice: cpuData.nice,
|
|
195
|
+
loadAvg: loadData ? [loadData.min1, loadData.min5, loadData.min15] : [0, 0, 0],
|
|
196
|
+
cores: cpuData.cpucore
|
|
197
|
+
};
|
|
198
|
+
const memory = {
|
|
199
|
+
percent: memData.percent,
|
|
200
|
+
totalBytes: memData.total,
|
|
201
|
+
usedBytes: memData.used,
|
|
202
|
+
availableBytes: memData.available,
|
|
203
|
+
swapUsedBytes: memData.swap_used ?? 0,
|
|
204
|
+
swapTotalBytes: memData.swap_total ?? 0
|
|
205
|
+
};
|
|
206
|
+
const disk = {
|
|
207
|
+
readBytes: diskIoData?.reduce((sum, d) => sum + (d.read_bytes ?? 0), 0) ?? 0,
|
|
208
|
+
writeBytes: diskIoData?.reduce((sum, d) => sum + (d.write_bytes ?? 0), 0) ?? 0,
|
|
209
|
+
readOps: diskIoData?.reduce((sum, d) => sum + (d.read_count ?? 0), 0) ?? 0,
|
|
210
|
+
writeOps: diskIoData?.reduce((sum, d) => sum + (d.write_count ?? 0), 0) ?? 0,
|
|
211
|
+
timestampMs: Date.now()
|
|
212
|
+
};
|
|
213
|
+
const network = {
|
|
214
|
+
rxBytes: networkData?.reduce((sum, n) => sum + (n.bytes_recv ?? 0), 0) ?? 0,
|
|
215
|
+
txBytes: networkData?.reduce((sum, n) => sum + (n.bytes_sent ?? 0), 0) ?? 0,
|
|
216
|
+
rxPackets: networkData?.reduce((sum, n) => sum + (n.packets_recv ?? 0), 0) ?? 0,
|
|
217
|
+
txPackets: networkData?.reduce((sum, n) => sum + (n.packets_sent ?? 0), 0) ?? 0,
|
|
218
|
+
rxErrors: networkData?.reduce((sum, n) => sum + (n.errin ?? 0), 0) ?? 0,
|
|
219
|
+
txErrors: networkData?.reduce((sum, n) => sum + (n.errout ?? 0), 0) ?? 0,
|
|
220
|
+
timestampMs: Date.now()
|
|
221
|
+
};
|
|
222
|
+
let gpu = null;
|
|
223
|
+
if (gpuData && gpuData.length > 0) {
|
|
224
|
+
const g = gpuData[0];
|
|
225
|
+
gpu = {
|
|
226
|
+
utilization: g.proc,
|
|
227
|
+
model: g.name,
|
|
228
|
+
memoryUsedBytes: 0,
|
|
229
|
+
memoryTotalBytes: 0,
|
|
230
|
+
temperature: g.temperature ?? null
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
let cpuTemperature = null;
|
|
234
|
+
if (sensorsData) {
|
|
235
|
+
const tempSensor = sensorsData.find(
|
|
236
|
+
(s) => s.type === "temperature_core" || s.label.toLowerCase().includes("cpu")
|
|
237
|
+
);
|
|
238
|
+
if (tempSensor) cpuTemperature = Math.round(tempSensor.value * 10) / 10;
|
|
239
|
+
}
|
|
240
|
+
const p = process;
|
|
241
|
+
const activeHandles = Array.isArray(p._getActiveHandles?.()) ? p._getActiveHandles().length : 0;
|
|
242
|
+
const activeRequests = Array.isArray(p._getActiveRequests?.()) ? p._getActiveRequests().length : 0;
|
|
243
|
+
const processInfo = {
|
|
244
|
+
openFds: 0,
|
|
245
|
+
threadCount: 0,
|
|
246
|
+
activeHandles,
|
|
247
|
+
activeRequests
|
|
248
|
+
};
|
|
249
|
+
return {
|
|
250
|
+
cpu,
|
|
251
|
+
memory,
|
|
252
|
+
gpu,
|
|
253
|
+
network,
|
|
254
|
+
disk,
|
|
255
|
+
pressure: { cpu: null, memory: null, io: null },
|
|
256
|
+
process: processInfo,
|
|
257
|
+
cpuTemperature,
|
|
258
|
+
timestampMs: Date.now()
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
// src/glances-metrics.addon.ts
|
|
264
|
+
function narrowWorkerState(state) {
|
|
265
|
+
switch (state) {
|
|
266
|
+
case "starting":
|
|
267
|
+
case "running":
|
|
268
|
+
case "stopping":
|
|
269
|
+
case "stopped":
|
|
270
|
+
case "crashed":
|
|
271
|
+
return state;
|
|
272
|
+
default:
|
|
273
|
+
return "running";
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
var GlancesMetricsAddon = class extends import_types.BaseAddon {
|
|
277
|
+
provider = null;
|
|
278
|
+
startedAtMs = Date.now();
|
|
279
|
+
constructor() {
|
|
280
|
+
super({ glancesUrl: "http://localhost:61208", timeoutMs: 3e3, samplingIntervalMs: 5e3 });
|
|
281
|
+
}
|
|
282
|
+
async onInitialize() {
|
|
283
|
+
const client = new GlancesClient(this.config.glancesUrl, this.config.timeoutMs);
|
|
284
|
+
const provider = new GlancesMetricsProvider(client, this.ctx.logger);
|
|
285
|
+
provider.startSampling(this.config.samplingIntervalMs);
|
|
286
|
+
this.provider = provider;
|
|
287
|
+
this.startedAtMs = Date.now();
|
|
288
|
+
const reachable = await client.isReachable();
|
|
289
|
+
if (reachable) {
|
|
290
|
+
this.ctx.logger.info("Glances metrics connected", { meta: { url: this.config.glancesUrl } });
|
|
291
|
+
} else {
|
|
292
|
+
this.ctx.logger.warn("Glances unreachable \u2014 calls will throw until Glances is available", { meta: { url: this.config.glancesUrl } });
|
|
293
|
+
}
|
|
294
|
+
const composed = {
|
|
295
|
+
collectSnapshot: () => provider.collectSnapshot(),
|
|
296
|
+
getCached: () => provider.getCached(),
|
|
297
|
+
getCurrent: () => provider.getCurrent(),
|
|
298
|
+
getDiskSpace: (params) => provider.getDiskSpace(params),
|
|
299
|
+
getGpuInfo: () => provider.getGpuInfo(),
|
|
300
|
+
getCpuTemperature: () => provider.getCpuTemperature(),
|
|
301
|
+
getProcessStats: (params) => provider.getProcessStats(params),
|
|
302
|
+
listAddonInstances: () => this.listAddonInstances(),
|
|
303
|
+
getAddonStats: (params) => this.getAddonStats(params.addonId),
|
|
304
|
+
// Glances doesn't own the host's process table — ghost detection
|
|
305
|
+
// lives in the native-metrics addon (PPID + $process scan). Glances
|
|
306
|
+
// can still provide CPU/memory snapshots while native-metrics
|
|
307
|
+
// handles the process inspector surface.
|
|
308
|
+
listNodeProcesses: async () => [],
|
|
309
|
+
killProcess: async () => ({ success: false, reason: "not supported by glances provider" })
|
|
310
|
+
};
|
|
311
|
+
return [{ capability: import_types.metricsProviderCapability, provider: composed }];
|
|
312
|
+
}
|
|
313
|
+
async onShutdown() {
|
|
314
|
+
this.provider?.stopSampling();
|
|
315
|
+
this.provider = null;
|
|
316
|
+
}
|
|
317
|
+
async onConfigChanged() {
|
|
318
|
+
if (this.provider) {
|
|
319
|
+
this.provider.stopSampling();
|
|
320
|
+
this.provider.startSampling(this.config.samplingIntervalMs);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
async listWorkerInstances() {
|
|
324
|
+
const broker = this.ctx.kernel.cluster?.broker;
|
|
325
|
+
if (!broker) return [];
|
|
326
|
+
try {
|
|
327
|
+
return await broker.call("$process.list");
|
|
328
|
+
} catch {
|
|
329
|
+
return [];
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
async listAddonInstances() {
|
|
333
|
+
const broker = this.ctx.kernel.cluster?.broker;
|
|
334
|
+
const hubNodeId = broker?.nodeID ?? "hub";
|
|
335
|
+
const uptimeSec = Math.max(0, Math.floor((Date.now() - this.startedAtMs) / 1e3));
|
|
336
|
+
const instances = [{
|
|
337
|
+
addonId: "$hub",
|
|
338
|
+
nodeId: hubNodeId,
|
|
339
|
+
role: "hub",
|
|
340
|
+
pid: process.pid,
|
|
341
|
+
state: "running",
|
|
342
|
+
uptimeSec
|
|
343
|
+
}];
|
|
344
|
+
const workers = await this.listWorkerInstances();
|
|
345
|
+
for (const w of workers) {
|
|
346
|
+
instances.push({
|
|
347
|
+
addonId: w.addonId,
|
|
348
|
+
nodeId: w.nodeId,
|
|
349
|
+
role: "worker",
|
|
350
|
+
pid: w.pid,
|
|
351
|
+
state: narrowWorkerState(w.state),
|
|
352
|
+
uptimeSec: w.uptimeSeconds
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
return instances;
|
|
356
|
+
}
|
|
357
|
+
async getAddonStats(addonId) {
|
|
358
|
+
const provider = this.provider;
|
|
359
|
+
if (!provider) return null;
|
|
360
|
+
if (addonId === "$hub") {
|
|
361
|
+
const stats2 = await provider.getProcessStats({ pids: [process.pid] });
|
|
362
|
+
return stats2[0] ?? null;
|
|
363
|
+
}
|
|
364
|
+
const workers = await this.listWorkerInstances();
|
|
365
|
+
const target = workers.find((w) => w.addonId === addonId);
|
|
366
|
+
if (!target) return null;
|
|
367
|
+
const stats = await provider.getProcessStats({ pids: [target.pid] });
|
|
368
|
+
return stats[0] ?? null;
|
|
369
|
+
}
|
|
370
|
+
globalSettingsSchema() {
|
|
371
|
+
return this.schema({
|
|
372
|
+
sections: [{
|
|
373
|
+
id: "glances-connection",
|
|
374
|
+
title: "Glances Metrics",
|
|
375
|
+
columns: 3,
|
|
376
|
+
fields: [
|
|
377
|
+
this.field({
|
|
378
|
+
type: "text",
|
|
379
|
+
key: "glancesUrl",
|
|
380
|
+
label: "Glances URL",
|
|
381
|
+
description: "Base URL of the Glances REST API",
|
|
382
|
+
placeholder: "http://localhost:61208",
|
|
383
|
+
default: "http://localhost:61208"
|
|
384
|
+
}),
|
|
385
|
+
this.field({
|
|
386
|
+
type: "number",
|
|
387
|
+
key: "timeoutMs",
|
|
388
|
+
label: "Request Timeout",
|
|
389
|
+
description: "Timeout for each Glances API request",
|
|
390
|
+
min: 500,
|
|
391
|
+
max: 1e4,
|
|
392
|
+
step: 500,
|
|
393
|
+
default: 3e3,
|
|
394
|
+
unit: "ms"
|
|
395
|
+
}),
|
|
396
|
+
this.field({
|
|
397
|
+
type: "number",
|
|
398
|
+
key: "samplingIntervalMs",
|
|
399
|
+
label: "Sampling Interval",
|
|
400
|
+
description: "How often to collect system metrics",
|
|
401
|
+
min: 1e3,
|
|
402
|
+
max: 6e4,
|
|
403
|
+
step: 1e3,
|
|
404
|
+
default: 5e3,
|
|
405
|
+
unit: "ms"
|
|
406
|
+
})
|
|
407
|
+
]
|
|
408
|
+
}]
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
};
|
|
412
|
+
//# sourceMappingURL=glances-metrics.addon.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/glances-metrics.addon.ts","../src/glances-client.ts","../src/glances-metrics-provider.ts"],"sourcesContent":["import type { AddonInstance, IMetricsProvider, ProviderRegistration } from '@camstack/types'\nimport { BaseAddon, metricsProviderCapability } from '@camstack/types'\nimport { GlancesClient } from './glances-client.js'\nimport { GlancesMetricsProvider } from './glances-metrics-provider.js'\n\ninterface GlancesConfig {\n readonly glancesUrl: string\n readonly timeoutMs: number\n readonly samplingIntervalMs: number\n}\n\ninterface KernelProcessInfo {\n readonly pid: number\n readonly nodeId: string\n readonly addonId: string\n readonly state: string\n readonly cpuPercent: number\n readonly memoryRss: number\n readonly uptimeSeconds: number\n}\n\ntype WorkerState = 'starting' | 'running' | 'stopping' | 'stopped' | 'crashed'\n\nfunction narrowWorkerState(state: string): WorkerState {\n switch (state) {\n case 'starting':\n case 'running':\n case 'stopping':\n case 'stopped':\n case 'crashed':\n return state\n default:\n return 'running'\n }\n}\n\n/**\n * Glances metrics — system metrics via the Glances REST API.\n *\n * No silent fallback: if Glances is unreachable the provider throws on every\n * call. Users wanting a Glances-free metrics source enable the native-metrics\n * addon instead.\n */\nexport default class GlancesMetricsAddon extends BaseAddon<GlancesConfig> {\n private provider: GlancesMetricsProvider | null = null\n private startedAtMs: number = Date.now()\n\n constructor() {\n super({ glancesUrl: 'http://localhost:61208', timeoutMs: 3000, samplingIntervalMs: 5000 })\n }\n\n protected async onInitialize(): Promise<ProviderRegistration[]> {\n const client = new GlancesClient(this.config.glancesUrl, this.config.timeoutMs)\n const provider = new GlancesMetricsProvider(client, this.ctx.logger)\n provider.startSampling(this.config.samplingIntervalMs)\n this.provider = provider\n this.startedAtMs = Date.now()\n\n const reachable = await client.isReachable()\n if (reachable) {\n this.ctx.logger.info('Glances metrics connected', { meta: { url: this.config.glancesUrl } })\n } else {\n this.ctx.logger.warn('Glances unreachable — calls will throw until Glances is available', { meta: { url: this.config.glancesUrl } })\n }\n\n const composed: IMetricsProvider = {\n collectSnapshot: () => provider.collectSnapshot(),\n getCached: () => provider.getCached(),\n getCurrent: () => provider.getCurrent(),\n getDiskSpace: (params) => provider.getDiskSpace(params),\n getGpuInfo: () => provider.getGpuInfo(),\n getCpuTemperature: () => provider.getCpuTemperature(),\n getProcessStats: (params) => provider.getProcessStats(params),\n listAddonInstances: () => this.listAddonInstances(),\n getAddonStats: (params) => this.getAddonStats(params.addonId),\n // Glances doesn't own the host's process table — ghost detection\n // lives in the native-metrics addon (PPID + $process scan). Glances\n // can still provide CPU/memory snapshots while native-metrics\n // handles the process inspector surface.\n listNodeProcesses: async () => [],\n killProcess: async () => ({ success: false, reason: 'not supported by glances provider' }),\n }\n return [{ capability: metricsProviderCapability, provider: composed }]\n }\n\n protected async onShutdown(): Promise<void> {\n this.provider?.stopSampling()\n this.provider = null\n }\n\n protected async onConfigChanged(): Promise<void> {\n if (this.provider) {\n this.provider.stopSampling()\n this.provider.startSampling(this.config.samplingIntervalMs)\n }\n }\n\n private async listWorkerInstances(): Promise<readonly KernelProcessInfo[]> {\n const broker = this.ctx.kernel.cluster?.broker\n if (!broker) return []\n try {\n return await broker.call<void, readonly KernelProcessInfo[]>('$process.list')\n } catch {\n return []\n }\n }\n\n private async listAddonInstances() {\n const broker = this.ctx.kernel.cluster?.broker\n const hubNodeId = broker?.nodeID ?? 'hub'\n const uptimeSec = Math.max(0, Math.floor((Date.now() - this.startedAtMs) / 1000))\n const instances: AddonInstance[] = [{\n addonId: '$hub',\n nodeId: hubNodeId,\n role: 'hub',\n pid: process.pid,\n state: 'running',\n uptimeSec,\n }]\n const workers = await this.listWorkerInstances()\n for (const w of workers) {\n instances.push({\n addonId: w.addonId,\n nodeId: w.nodeId,\n role: 'worker',\n pid: w.pid,\n state: narrowWorkerState(w.state),\n uptimeSec: w.uptimeSeconds,\n })\n }\n return instances\n }\n\n private async getAddonStats(addonId: string) {\n const provider = this.provider\n if (!provider) return null\n if (addonId === '$hub') {\n const stats = await provider.getProcessStats({ pids: [process.pid] })\n return stats[0] ?? null\n }\n const workers = await this.listWorkerInstances()\n const target = workers.find((w) => w.addonId === addonId)\n if (!target) return null\n const stats = await provider.getProcessStats({ pids: [target.pid] })\n return stats[0] ?? null\n }\n\n protected globalSettingsSchema() {\n return this.schema({\n sections: [{\n id: 'glances-connection',\n title: 'Glances Metrics',\n columns: 3,\n fields: [\n this.field({\n type: 'text', key: 'glancesUrl', label: 'Glances URL',\n description: 'Base URL of the Glances REST API',\n placeholder: 'http://localhost:61208', default: 'http://localhost:61208',\n }),\n this.field({\n type: 'number', key: 'timeoutMs', label: 'Request Timeout',\n description: 'Timeout for each Glances API request',\n min: 500, max: 10000, step: 500, default: 3000, unit: 'ms',\n }),\n this.field({\n type: 'number', key: 'samplingIntervalMs', label: 'Sampling Interval',\n description: 'How often to collect system metrics',\n min: 1000, max: 60000, step: 1000, default: 5000, unit: 'ms',\n }),\n ],\n }],\n })\n }\n}\n","// ===========================================================================\n// GlancesClient — HTTP client for Glances REST API v4.\n// Endpoint reference: http://localhost:61208/api/4/\n// ===========================================================================\n\nexport interface GlancesCpuResponse {\n total: number\n user: number\n system: number\n idle: number\n nice: number\n irq: number\n iowait?: number\n steal?: number\n guest?: number\n cpucore: number\n}\n\nexport interface GlancesMemResponse {\n total: number\n available: number\n percent: number\n used: number\n free: number\n active?: number\n inactive?: number\n buffers?: number\n cached?: number\n shared?: number\n swap_total?: number\n swap_used?: number\n swap_free?: number\n swap_percent?: number\n}\n\nexport interface GlancesDiskIoItem {\n disk_name: string\n read_bytes: number\n write_bytes: number\n read_count: number\n write_count: number\n key: string\n}\n\nexport interface GlancesFsItem {\n device_name: string\n fs_type: string\n mnt_point: string\n size: number\n used: number\n free: number\n percent: number\n key: string\n}\n\nexport interface GlancesNetworkItem {\n interface_name: string\n bytes_recv: number\n bytes_sent: number\n packets_recv?: number\n packets_sent?: number\n errin?: number\n errout?: number\n key: string\n}\n\nexport interface GlancesSensorItem {\n label: string\n value: number\n unit: string\n type: string // 'temperature_core', 'fan_speed', 'battery', etc.\n key: string\n}\n\nexport interface GlancesGpuItem {\n gpu_id: number\n name: string\n mem: number // memory usage %\n proc: number // processor usage %\n temperature?: number\n fan_speed?: number\n key: string\n}\n\nexport interface GlancesLoadResponse {\n min1: number\n min5: number\n min15: number\n cpucore: number\n}\n\nexport class GlancesClient {\n constructor(\n private readonly baseUrl: string,\n private readonly timeoutMs: number = 3000,\n ) {}\n\n /** Check if Glances is reachable. */\n async isReachable(): Promise<boolean> {\n try {\n const res = await this.fetch('/status')\n return res !== null\n } catch {\n return false\n }\n }\n\n async getCpu(): Promise<GlancesCpuResponse | null> {\n return this.fetch<GlancesCpuResponse>('/cpu')\n }\n\n async getMem(): Promise<GlancesMemResponse | null> {\n return this.fetch<GlancesMemResponse>('/mem')\n }\n\n async getDiskIo(): Promise<GlancesDiskIoItem[] | null> {\n return this.fetch<GlancesDiskIoItem[]>('/diskio')\n }\n\n async getFs(): Promise<GlancesFsItem[] | null> {\n return this.fetch<GlancesFsItem[]>('/fs')\n }\n\n async getNetwork(): Promise<GlancesNetworkItem[] | null> {\n return this.fetch<GlancesNetworkItem[]>('/network')\n }\n\n async getSensors(): Promise<GlancesSensorItem[] | null> {\n return this.fetch<GlancesSensorItem[]>('/sensors')\n }\n\n async getGpu(): Promise<GlancesGpuItem[] | null> {\n return this.fetch<GlancesGpuItem[]>('/gpu')\n }\n\n async getLoad(): Promise<GlancesLoadResponse | null> {\n return this.fetch<GlancesLoadResponse>('/load')\n }\n\n // ─── Internal ─────────────────────────────────────────────────────\n\n private async fetch<T>(endpoint: string): Promise<T | null> {\n try {\n const url = `${this.baseUrl}/api/4${endpoint}`\n const res = await globalThis.fetch(url, {\n signal: AbortSignal.timeout(this.timeoutMs),\n headers: { 'Accept': 'application/json' },\n })\n if (!res.ok) return null\n return await res.json() as T\n } catch {\n return null\n }\n }\n}\n","import type {\n SystemResourceSnapshot,\n CpuBreakdown,\n MemoryInfo,\n DiskSpaceInfo,\n DiskIoSnapshot,\n NetworkIoSnapshot,\n MetricsGpuInfo,\n ProcessResourceInfo,\n PidResourceStats,\n IScopedLogger,\n} from '@camstack/types'\nimport type { GlancesClient } from './glances-client.js'\n\n/**\n * GlancesMetricsProvider — IMetricsProvider backed by the Glances REST API.\n *\n * There is no silent fallback: if Glances is unreachable each method throws\n * so the caller sees the real failure. Users who want a non-Glances metrics\n * source enable the native-metrics addon instead.\n */\nexport class GlancesMetricsProvider {\n readonly id = 'glances'\n\n private cachedSnapshot: SystemResourceSnapshot | null = null\n private samplingTimer: ReturnType<typeof setInterval> | null = null\n\n constructor(\n private readonly client: GlancesClient,\n private readonly logger: IScopedLogger,\n ) {}\n\n // ─── Lifecycle (addon-owned, not on the cap contract) ───────────────\n\n startSampling(intervalMs: number): void {\n this.collectSnapshot().then((s) => { this.cachedSnapshot = s }).catch((err: unknown) => {\n this.logger.warn('Initial Glances snapshot failed', { meta: { error: err instanceof Error ? err.message : String(err) } })\n })\n\n this.samplingTimer = setInterval(() => {\n this.collectSnapshot().then((s) => { this.cachedSnapshot = s }).catch(() => { /* next tick */ })\n }, intervalMs)\n if (this.samplingTimer.unref) this.samplingTimer.unref()\n }\n\n stopSampling(): void {\n if (this.samplingTimer) { clearInterval(this.samplingTimer); this.samplingTimer = null }\n }\n\n // ─── Cap contract ───────────────────────────────────────────────────\n\n async collectSnapshot(): Promise<SystemResourceSnapshot> {\n const snapshot = await this.collectFromGlances()\n this.cachedSnapshot = snapshot\n return snapshot\n }\n\n async getCached(): Promise<SystemResourceSnapshot | null> {\n return this.cachedSnapshot\n }\n\n async getCurrent(): Promise<{\n cpuPercent: number\n memoryPercent: number\n memoryUsedMB: number\n memoryTotalMB: number\n diskPercent?: number\n temperature?: number\n gpuPercent?: number\n gpuMemoryPercent?: number\n }> {\n const s = this.cachedSnapshot ?? await this.collectSnapshot()\n const mb = (bytes: number) => Math.round(bytes / 1024 / 1024)\n return {\n cpuPercent: s.cpu.total,\n memoryPercent: s.memory.percent,\n memoryUsedMB: mb(s.memory.usedBytes),\n memoryTotalMB: mb(s.memory.totalBytes),\n temperature: s.cpuTemperature ?? undefined,\n gpuPercent: s.gpu?.utilization,\n gpuMemoryPercent:\n s.gpu && s.gpu.memoryTotalBytes > 0\n ? Math.round((s.gpu.memoryUsedBytes / s.gpu.memoryTotalBytes) * 1000) / 10\n : undefined,\n }\n }\n\n async getDiskSpace(input: { dirPath: string }): Promise<DiskSpaceInfo> {\n const fsData = await this.client.getFs()\n if (!fsData) throw new Error('Glances /fs endpoint returned no data')\n const match = fsData.find((f) => input.dirPath.startsWith(f.mnt_point))\n if (!match) throw new Error('Glances reported no filesystem matching path: ' + input.dirPath)\n return {\n path: match.mnt_point,\n totalBytes: match.size,\n usedBytes: match.used,\n availableBytes: match.free,\n percent: match.percent,\n }\n }\n\n async getGpuInfo(): Promise<MetricsGpuInfo | null> {\n const gpuData = await this.client.getGpu()\n if (!gpuData || gpuData.length === 0) return null\n const gpu = gpuData[0]!\n return {\n utilization: gpu.proc,\n model: gpu.name,\n memoryUsedBytes: 0,\n memoryTotalBytes: 0,\n temperature: gpu.temperature ?? null,\n }\n }\n\n async getCpuTemperature(): Promise<number | null> {\n const sensors = await this.client.getSensors()\n if (!sensors) return null\n const cpuTemp = sensors.find((s) =>\n s.type === 'temperature_core' || s.label.toLowerCase().includes('cpu'),\n )\n return cpuTemp ? Math.round(cpuTemp.value * 10) / 10 : null\n }\n\n async getProcessStats(_input: { pids: number[] }): Promise<PidResourceStats[]> {\n // Glances does not expose per-PID CPU/mem the way this capability requires.\n // Consumers wanting per-process stats enable the native-metrics addon.\n return []\n }\n\n // ─── Internal ───────────────────────────────────────────────────────\n\n private async collectFromGlances(): Promise<SystemResourceSnapshot> {\n const [cpuData, memData, diskIoData, networkData, loadData, sensorsData, gpuData] = await Promise.all([\n this.client.getCpu(),\n this.client.getMem(),\n this.client.getDiskIo(),\n this.client.getNetwork(),\n this.client.getLoad(),\n this.client.getSensors(),\n this.client.getGpu(),\n ])\n\n if (!cpuData || !memData) {\n throw new Error('Glances: core endpoints (cpu/mem) returned null')\n }\n\n const cpu: CpuBreakdown = {\n total: cpuData.total,\n user: cpuData.user,\n system: cpuData.system,\n irq: cpuData.irq,\n nice: cpuData.nice,\n loadAvg: loadData\n ? [loadData.min1, loadData.min5, loadData.min15]\n : [0, 0, 0],\n cores: cpuData.cpucore,\n }\n\n const memory: MemoryInfo = {\n percent: memData.percent,\n totalBytes: memData.total,\n usedBytes: memData.used,\n availableBytes: memData.available,\n swapUsedBytes: memData.swap_used ?? 0,\n swapTotalBytes: memData.swap_total ?? 0,\n }\n\n const disk: DiskIoSnapshot = {\n readBytes: diskIoData?.reduce((sum, d) => sum + (d.read_bytes ?? 0), 0) ?? 0,\n writeBytes: diskIoData?.reduce((sum, d) => sum + (d.write_bytes ?? 0), 0) ?? 0,\n readOps: diskIoData?.reduce((sum, d) => sum + (d.read_count ?? 0), 0) ?? 0,\n writeOps: diskIoData?.reduce((sum, d) => sum + (d.write_count ?? 0), 0) ?? 0,\n timestampMs: Date.now(),\n }\n\n const network: NetworkIoSnapshot = {\n rxBytes: networkData?.reduce((sum, n) => sum + (n.bytes_recv ?? 0), 0) ?? 0,\n txBytes: networkData?.reduce((sum, n) => sum + (n.bytes_sent ?? 0), 0) ?? 0,\n rxPackets: networkData?.reduce((sum, n) => sum + (n.packets_recv ?? 0), 0) ?? 0,\n txPackets: networkData?.reduce((sum, n) => sum + (n.packets_sent ?? 0), 0) ?? 0,\n rxErrors: networkData?.reduce((sum, n) => sum + (n.errin ?? 0), 0) ?? 0,\n txErrors: networkData?.reduce((sum, n) => sum + (n.errout ?? 0), 0) ?? 0,\n timestampMs: Date.now(),\n }\n\n let gpu: MetricsGpuInfo | null = null\n if (gpuData && gpuData.length > 0) {\n const g = gpuData[0]!\n gpu = {\n utilization: g.proc,\n model: g.name,\n memoryUsedBytes: 0,\n memoryTotalBytes: 0,\n temperature: g.temperature ?? null,\n }\n }\n\n let cpuTemperature: number | null = null\n if (sensorsData) {\n const tempSensor = sensorsData.find((s) =>\n s.type === 'temperature_core' || s.label.toLowerCase().includes('cpu'),\n )\n if (tempSensor) cpuTemperature = Math.round(tempSensor.value * 10) / 10\n }\n\n // Node.js internals _getActiveHandles / _getActiveRequests are undocumented\n // but stable across all LTS versions.\n const p = process as unknown as Record<string, (() => unknown[]) | undefined>\n const activeHandles = Array.isArray(p._getActiveHandles?.()) ? (p._getActiveHandles!() as unknown[]).length : 0\n const activeRequests = Array.isArray(p._getActiveRequests?.()) ? (p._getActiveRequests!() as unknown[]).length : 0\n const processInfo: ProcessResourceInfo = {\n openFds: 0,\n threadCount: 0,\n activeHandles,\n activeRequests,\n }\n\n return {\n cpu,\n memory,\n gpu,\n network,\n disk,\n pressure: { cpu: null, memory: null, io: null },\n process: processInfo,\n cpuTemperature,\n timestampMs: Date.now(),\n }\n }\n}\n"],"mappings":"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","names":["stats"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
export { default as GlancesMetricsAddon } from './glances-metrics.addon.mjs';
|
|
2
|
+
import { IScopedLogger, SystemResourceSnapshot, DiskSpaceInfo, MetricsGpuInfo, PidResourceStats } from '@camstack/types';
|
|
3
|
+
|
|
4
|
+
interface GlancesCpuResponse {
|
|
5
|
+
total: number;
|
|
6
|
+
user: number;
|
|
7
|
+
system: number;
|
|
8
|
+
idle: number;
|
|
9
|
+
nice: number;
|
|
10
|
+
irq: number;
|
|
11
|
+
iowait?: number;
|
|
12
|
+
steal?: number;
|
|
13
|
+
guest?: number;
|
|
14
|
+
cpucore: number;
|
|
15
|
+
}
|
|
16
|
+
interface GlancesMemResponse {
|
|
17
|
+
total: number;
|
|
18
|
+
available: number;
|
|
19
|
+
percent: number;
|
|
20
|
+
used: number;
|
|
21
|
+
free: number;
|
|
22
|
+
active?: number;
|
|
23
|
+
inactive?: number;
|
|
24
|
+
buffers?: number;
|
|
25
|
+
cached?: number;
|
|
26
|
+
shared?: number;
|
|
27
|
+
swap_total?: number;
|
|
28
|
+
swap_used?: number;
|
|
29
|
+
swap_free?: number;
|
|
30
|
+
swap_percent?: number;
|
|
31
|
+
}
|
|
32
|
+
interface GlancesDiskIoItem {
|
|
33
|
+
disk_name: string;
|
|
34
|
+
read_bytes: number;
|
|
35
|
+
write_bytes: number;
|
|
36
|
+
read_count: number;
|
|
37
|
+
write_count: number;
|
|
38
|
+
key: string;
|
|
39
|
+
}
|
|
40
|
+
interface GlancesFsItem {
|
|
41
|
+
device_name: string;
|
|
42
|
+
fs_type: string;
|
|
43
|
+
mnt_point: string;
|
|
44
|
+
size: number;
|
|
45
|
+
used: number;
|
|
46
|
+
free: number;
|
|
47
|
+
percent: number;
|
|
48
|
+
key: string;
|
|
49
|
+
}
|
|
50
|
+
interface GlancesNetworkItem {
|
|
51
|
+
interface_name: string;
|
|
52
|
+
bytes_recv: number;
|
|
53
|
+
bytes_sent: number;
|
|
54
|
+
packets_recv?: number;
|
|
55
|
+
packets_sent?: number;
|
|
56
|
+
errin?: number;
|
|
57
|
+
errout?: number;
|
|
58
|
+
key: string;
|
|
59
|
+
}
|
|
60
|
+
interface GlancesSensorItem {
|
|
61
|
+
label: string;
|
|
62
|
+
value: number;
|
|
63
|
+
unit: string;
|
|
64
|
+
type: string;
|
|
65
|
+
key: string;
|
|
66
|
+
}
|
|
67
|
+
interface GlancesGpuItem {
|
|
68
|
+
gpu_id: number;
|
|
69
|
+
name: string;
|
|
70
|
+
mem: number;
|
|
71
|
+
proc: number;
|
|
72
|
+
temperature?: number;
|
|
73
|
+
fan_speed?: number;
|
|
74
|
+
key: string;
|
|
75
|
+
}
|
|
76
|
+
interface GlancesLoadResponse {
|
|
77
|
+
min1: number;
|
|
78
|
+
min5: number;
|
|
79
|
+
min15: number;
|
|
80
|
+
cpucore: number;
|
|
81
|
+
}
|
|
82
|
+
declare class GlancesClient {
|
|
83
|
+
private readonly baseUrl;
|
|
84
|
+
private readonly timeoutMs;
|
|
85
|
+
constructor(baseUrl: string, timeoutMs?: number);
|
|
86
|
+
/** Check if Glances is reachable. */
|
|
87
|
+
isReachable(): Promise<boolean>;
|
|
88
|
+
getCpu(): Promise<GlancesCpuResponse | null>;
|
|
89
|
+
getMem(): Promise<GlancesMemResponse | null>;
|
|
90
|
+
getDiskIo(): Promise<GlancesDiskIoItem[] | null>;
|
|
91
|
+
getFs(): Promise<GlancesFsItem[] | null>;
|
|
92
|
+
getNetwork(): Promise<GlancesNetworkItem[] | null>;
|
|
93
|
+
getSensors(): Promise<GlancesSensorItem[] | null>;
|
|
94
|
+
getGpu(): Promise<GlancesGpuItem[] | null>;
|
|
95
|
+
getLoad(): Promise<GlancesLoadResponse | null>;
|
|
96
|
+
private fetch;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* GlancesMetricsProvider — IMetricsProvider backed by the Glances REST API.
|
|
101
|
+
*
|
|
102
|
+
* There is no silent fallback: if Glances is unreachable each method throws
|
|
103
|
+
* so the caller sees the real failure. Users who want a non-Glances metrics
|
|
104
|
+
* source enable the native-metrics addon instead.
|
|
105
|
+
*/
|
|
106
|
+
declare class GlancesMetricsProvider {
|
|
107
|
+
private readonly client;
|
|
108
|
+
private readonly logger;
|
|
109
|
+
readonly id = "glances";
|
|
110
|
+
private cachedSnapshot;
|
|
111
|
+
private samplingTimer;
|
|
112
|
+
constructor(client: GlancesClient, logger: IScopedLogger);
|
|
113
|
+
startSampling(intervalMs: number): void;
|
|
114
|
+
stopSampling(): void;
|
|
115
|
+
collectSnapshot(): Promise<SystemResourceSnapshot>;
|
|
116
|
+
getCached(): Promise<SystemResourceSnapshot | null>;
|
|
117
|
+
getCurrent(): Promise<{
|
|
118
|
+
cpuPercent: number;
|
|
119
|
+
memoryPercent: number;
|
|
120
|
+
memoryUsedMB: number;
|
|
121
|
+
memoryTotalMB: number;
|
|
122
|
+
diskPercent?: number;
|
|
123
|
+
temperature?: number;
|
|
124
|
+
gpuPercent?: number;
|
|
125
|
+
gpuMemoryPercent?: number;
|
|
126
|
+
}>;
|
|
127
|
+
getDiskSpace(input: {
|
|
128
|
+
dirPath: string;
|
|
129
|
+
}): Promise<DiskSpaceInfo>;
|
|
130
|
+
getGpuInfo(): Promise<MetricsGpuInfo | null>;
|
|
131
|
+
getCpuTemperature(): Promise<number | null>;
|
|
132
|
+
getProcessStats(_input: {
|
|
133
|
+
pids: number[];
|
|
134
|
+
}): Promise<PidResourceStats[]>;
|
|
135
|
+
private collectFromGlances;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export { GlancesClient, GlancesMetricsProvider };
|