vibora 1.7.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/index-BxOt05pH.css +1 -0
- package/dist/assets/index-DP-OAlLv.js +116 -0
- package/dist/index.html +2 -2
- package/drizzle/0005_zippy_professor_monster.sql +1 -0
- package/drizzle/meta/0005_snapshot.json +472 -0
- package/drizzle/meta/_journal.json +7 -0
- package/package.json +1 -1
- package/server/index.js +200 -11
package/server/index.js
CHANGED
|
@@ -7732,6 +7732,7 @@ var systemMetrics = sqliteTable("system_metrics", {
|
|
|
7732
7732
|
cpuPercent: real("cpu_percent").notNull(),
|
|
7733
7733
|
memoryUsedBytes: integer("memory_used_bytes").notNull(),
|
|
7734
7734
|
memoryTotalBytes: integer("memory_total_bytes").notNull(),
|
|
7735
|
+
memoryCacheBytes: integer("memory_cache_bytes").notNull().default(0),
|
|
7735
7736
|
diskUsedBytes: integer("disk_used_bytes").notNull(),
|
|
7736
7737
|
diskTotalBytes: integer("disk_total_bytes").notNull()
|
|
7737
7738
|
});
|
|
@@ -143455,9 +143456,47 @@ import { execSync as execSync5 } from "child_process";
|
|
|
143455
143456
|
|
|
143456
143457
|
// server/services/metrics-collector.ts
|
|
143457
143458
|
import os5 from "os";
|
|
143459
|
+
import fs7 from "fs";
|
|
143458
143460
|
import { execSync as execSync4 } from "child_process";
|
|
143459
143461
|
var COLLECT_INTERVAL = 5000;
|
|
143460
143462
|
var RETENTION_HOURS = 24;
|
|
143463
|
+
function getMemoryInfo() {
|
|
143464
|
+
const total = os5.totalmem();
|
|
143465
|
+
try {
|
|
143466
|
+
const meminfo = fs7.readFileSync("/proc/meminfo", "utf-8");
|
|
143467
|
+
const values = {};
|
|
143468
|
+
for (const line of meminfo.split(`
|
|
143469
|
+
`)) {
|
|
143470
|
+
const match3 = line.match(/^(\w+):\s+(\d+)\s+kB/);
|
|
143471
|
+
if (match3) {
|
|
143472
|
+
values[match3[1]] = parseInt(match3[2], 10) * 1024;
|
|
143473
|
+
}
|
|
143474
|
+
}
|
|
143475
|
+
const memTotal = values["MemTotal"] || total;
|
|
143476
|
+
const memFree = values["MemFree"] || 0;
|
|
143477
|
+
const buffers = values["Buffers"] || 0;
|
|
143478
|
+
const cached = values["Cached"] || 0;
|
|
143479
|
+
const sReclaimable = values["SReclaimable"] || 0;
|
|
143480
|
+
const shmem = values["Shmem"] || 0;
|
|
143481
|
+
let cacheBuffers = buffers + cached + sReclaimable - shmem;
|
|
143482
|
+
if (cacheBuffers < 0) {
|
|
143483
|
+
cacheBuffers = 0;
|
|
143484
|
+
}
|
|
143485
|
+
const used = memTotal - memFree - buffers - cached - sReclaimable + shmem;
|
|
143486
|
+
return {
|
|
143487
|
+
total: memTotal,
|
|
143488
|
+
used: Math.max(used, 0),
|
|
143489
|
+
cache: cacheBuffers
|
|
143490
|
+
};
|
|
143491
|
+
} catch {
|
|
143492
|
+
const free = os5.freemem();
|
|
143493
|
+
return {
|
|
143494
|
+
total,
|
|
143495
|
+
used: total - free,
|
|
143496
|
+
cache: 0
|
|
143497
|
+
};
|
|
143498
|
+
}
|
|
143499
|
+
}
|
|
143461
143500
|
var intervalId = null;
|
|
143462
143501
|
var previousCpu = null;
|
|
143463
143502
|
function getCpuSnapshot() {
|
|
@@ -143505,15 +143544,14 @@ function getDiskUsage() {
|
|
|
143505
143544
|
function collectMetrics() {
|
|
143506
143545
|
const timestamp = Math.floor(Date.now() / 1000);
|
|
143507
143546
|
const cpuPercent = calculateCpuPercent();
|
|
143508
|
-
const
|
|
143509
|
-
const memoryFree = os5.freemem();
|
|
143510
|
-
const memoryUsed = memoryTotal - memoryFree;
|
|
143547
|
+
const memory = getMemoryInfo();
|
|
143511
143548
|
const disk = getDiskUsage();
|
|
143512
143549
|
db.insert(systemMetrics).values({
|
|
143513
143550
|
timestamp,
|
|
143514
143551
|
cpuPercent,
|
|
143515
|
-
memoryUsedBytes:
|
|
143516
|
-
memoryTotalBytes:
|
|
143552
|
+
memoryUsedBytes: memory.used,
|
|
143553
|
+
memoryTotalBytes: memory.total,
|
|
143554
|
+
memoryCacheBytes: memory.cache,
|
|
143517
143555
|
diskUsedBytes: disk.used,
|
|
143518
143556
|
diskTotalBytes: disk.total
|
|
143519
143557
|
}).run();
|
|
@@ -143555,21 +143593,22 @@ function getMetrics(windowSeconds) {
|
|
|
143555
143593
|
timestamp: row.timestamp,
|
|
143556
143594
|
cpuPercent: row.cpuPercent,
|
|
143557
143595
|
memoryUsedPercent: row.memoryTotalBytes > 0 ? row.memoryUsedBytes / row.memoryTotalBytes * 100 : 0,
|
|
143596
|
+
memoryCachePercent: row.memoryTotalBytes > 0 ? row.memoryCacheBytes / row.memoryTotalBytes * 100 : 0,
|
|
143558
143597
|
diskUsedPercent: row.diskTotalBytes > 0 ? row.diskUsedBytes / row.diskTotalBytes * 100 : 0
|
|
143559
143598
|
}));
|
|
143560
143599
|
}
|
|
143561
143600
|
function getCurrentMetrics() {
|
|
143562
|
-
const
|
|
143563
|
-
const memoryFree = os5.freemem();
|
|
143564
|
-
const memoryUsed = memoryTotal - memoryFree;
|
|
143601
|
+
const memory = getMemoryInfo();
|
|
143565
143602
|
const disk = getDiskUsage();
|
|
143566
143603
|
const latest = db.select().from(systemMetrics).orderBy(systemMetrics.timestamp).limit(1).all();
|
|
143567
143604
|
return {
|
|
143568
143605
|
cpu: latest.length > 0 ? latest[0].cpuPercent : 0,
|
|
143569
143606
|
memory: {
|
|
143570
|
-
total:
|
|
143571
|
-
used:
|
|
143572
|
-
|
|
143607
|
+
total: memory.total,
|
|
143608
|
+
used: memory.used,
|
|
143609
|
+
cache: memory.cache,
|
|
143610
|
+
usedPercent: memory.total > 0 ? memory.used / memory.total * 100 : 0,
|
|
143611
|
+
cachePercent: memory.total > 0 ? memory.cache / memory.total * 100 : 0
|
|
143573
143612
|
},
|
|
143574
143613
|
disk: {
|
|
143575
143614
|
total: disk.total,
|
|
@@ -143797,6 +143836,156 @@ monitoringRoutes.post("/claude-instances/:pid/kill-pid", (c) => {
|
|
|
143797
143836
|
return c.json({ error: message }, 500);
|
|
143798
143837
|
}
|
|
143799
143838
|
});
|
|
143839
|
+
monitoringRoutes.get("/top-processes", (c) => {
|
|
143840
|
+
const sortBy = c.req.query("sort") || "memory";
|
|
143841
|
+
const limit = parseInt(c.req.query("limit") || "10", 10);
|
|
143842
|
+
try {
|
|
143843
|
+
const memTotal = parseInt(readFileSync7("/proc/meminfo", "utf-8").match(/MemTotal:\s+(\d+)/)?.[1] || "0", 10) * 1024;
|
|
143844
|
+
const processes = [];
|
|
143845
|
+
const procDirs = readdirSync7("/proc").filter((d) => /^\d+$/.test(d));
|
|
143846
|
+
for (const pidStr of procDirs) {
|
|
143847
|
+
const pid = parseInt(pidStr, 10);
|
|
143848
|
+
try {
|
|
143849
|
+
const status = readFileSync7(`/proc/${pid}/status`, "utf-8");
|
|
143850
|
+
const nameMatch = status.match(/Name:\s+(.+)/);
|
|
143851
|
+
const rssMatch = status.match(/VmRSS:\s+(\d+)\s+kB/);
|
|
143852
|
+
if (!nameMatch || !rssMatch)
|
|
143853
|
+
continue;
|
|
143854
|
+
const name = nameMatch[1].trim();
|
|
143855
|
+
const memoryKB = parseInt(rssMatch[1], 10);
|
|
143856
|
+
const memoryMB = memoryKB / 1024;
|
|
143857
|
+
const memoryPercent = memTotal > 0 ? memoryKB * 1024 / memTotal * 100 : 0;
|
|
143858
|
+
let command = "";
|
|
143859
|
+
try {
|
|
143860
|
+
command = readFileSync7(`/proc/${pid}/cmdline`, "utf-8").replace(/\0/g, " ").trim().slice(0, 200);
|
|
143861
|
+
} catch {
|
|
143862
|
+
command = name;
|
|
143863
|
+
}
|
|
143864
|
+
const cpuPercent = 0;
|
|
143865
|
+
processes.push({
|
|
143866
|
+
pid,
|
|
143867
|
+
name,
|
|
143868
|
+
command,
|
|
143869
|
+
cpuPercent: Math.round(cpuPercent * 10) / 10,
|
|
143870
|
+
memoryMB: Math.round(memoryMB * 10) / 10,
|
|
143871
|
+
memoryPercent: Math.round(memoryPercent * 10) / 10
|
|
143872
|
+
});
|
|
143873
|
+
} catch {
|
|
143874
|
+
continue;
|
|
143875
|
+
}
|
|
143876
|
+
}
|
|
143877
|
+
if (sortBy === "cpu") {
|
|
143878
|
+
processes.sort((a, b) => b.cpuPercent - a.cpuPercent);
|
|
143879
|
+
} else {
|
|
143880
|
+
processes.sort((a, b) => b.memoryMB - a.memoryMB);
|
|
143881
|
+
}
|
|
143882
|
+
return c.json(processes.slice(0, limit));
|
|
143883
|
+
} catch {
|
|
143884
|
+
try {
|
|
143885
|
+
const sortFlag = sortBy === "cpu" ? "-pcpu" : "-rss";
|
|
143886
|
+
const result = execSync5(`ps -eo pid,comm,args,%cpu,rss --sort=${sortFlag} --no-headers | head -${limit + 1}`, { encoding: "utf-8", timeout: 5000 });
|
|
143887
|
+
const memTotal = parseInt(execSync5("grep MemTotal /proc/meminfo", { encoding: "utf-8" }).match(/(\d+)/)?.[1] || "0", 10) * 1024;
|
|
143888
|
+
const processes = [];
|
|
143889
|
+
for (const line of result.trim().split(`
|
|
143890
|
+
`)) {
|
|
143891
|
+
const match3 = line.match(/^\s*(\d+)\s+(\S+)\s+(.+?)\s+([\d.]+)\s+(\d+)\s*$/);
|
|
143892
|
+
if (match3) {
|
|
143893
|
+
const memoryKB = parseInt(match3[5], 10);
|
|
143894
|
+
processes.push({
|
|
143895
|
+
pid: parseInt(match3[1], 10),
|
|
143896
|
+
name: match3[2],
|
|
143897
|
+
command: match3[3].trim().slice(0, 200),
|
|
143898
|
+
cpuPercent: parseFloat(match3[4]),
|
|
143899
|
+
memoryMB: Math.round(memoryKB / 1024 * 10) / 10,
|
|
143900
|
+
memoryPercent: memTotal > 0 ? Math.round(memoryKB * 1024 / memTotal * 1000) / 10 : 0
|
|
143901
|
+
});
|
|
143902
|
+
}
|
|
143903
|
+
}
|
|
143904
|
+
return c.json(processes);
|
|
143905
|
+
} catch (fallbackErr) {
|
|
143906
|
+
const message = fallbackErr instanceof Error ? fallbackErr.message : String(fallbackErr);
|
|
143907
|
+
return c.json({ error: message }, 500);
|
|
143908
|
+
}
|
|
143909
|
+
}
|
|
143910
|
+
});
|
|
143911
|
+
monitoringRoutes.get("/docker-stats", (c) => {
|
|
143912
|
+
try {
|
|
143913
|
+
let result;
|
|
143914
|
+
let runtime = "docker";
|
|
143915
|
+
try {
|
|
143916
|
+
result = execSync5('docker stats --no-stream --format "{{json .}}"', {
|
|
143917
|
+
encoding: "utf-8",
|
|
143918
|
+
timeout: 1e4,
|
|
143919
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
143920
|
+
});
|
|
143921
|
+
} catch {
|
|
143922
|
+
try {
|
|
143923
|
+
result = execSync5('podman stats --no-stream --format "{{json .}}"', {
|
|
143924
|
+
encoding: "utf-8",
|
|
143925
|
+
timeout: 1e4,
|
|
143926
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
143927
|
+
});
|
|
143928
|
+
runtime = "podman";
|
|
143929
|
+
} catch {
|
|
143930
|
+
return c.json({ containers: [], available: false, runtime: null });
|
|
143931
|
+
}
|
|
143932
|
+
}
|
|
143933
|
+
const containers = [];
|
|
143934
|
+
for (const line of result.trim().split(`
|
|
143935
|
+
`)) {
|
|
143936
|
+
if (!line.trim())
|
|
143937
|
+
continue;
|
|
143938
|
+
try {
|
|
143939
|
+
const data = JSON.parse(line);
|
|
143940
|
+
const cpuStr = data.CPUPerc || "0%";
|
|
143941
|
+
const cpuPercent = parseFloat(cpuStr.replace("%", "")) || 0;
|
|
143942
|
+
const memUsageStr = data.MemUsage || "0B / 0B";
|
|
143943
|
+
const [usedStr, limitStr] = memUsageStr.split(" / ");
|
|
143944
|
+
const parseMemory = (str) => {
|
|
143945
|
+
const match3 = str.match(/([\d.]+)\s*(B|KB|KiB|MB|MiB|GB|GiB)/i);
|
|
143946
|
+
if (!match3)
|
|
143947
|
+
return 0;
|
|
143948
|
+
const value = parseFloat(match3[1]);
|
|
143949
|
+
const unit = match3[2].toLowerCase();
|
|
143950
|
+
switch (unit) {
|
|
143951
|
+
case "b":
|
|
143952
|
+
return value / (1024 * 1024);
|
|
143953
|
+
case "kb":
|
|
143954
|
+
case "kib":
|
|
143955
|
+
return value / 1024;
|
|
143956
|
+
case "mb":
|
|
143957
|
+
case "mib":
|
|
143958
|
+
return value;
|
|
143959
|
+
case "gb":
|
|
143960
|
+
case "gib":
|
|
143961
|
+
return value * 1024;
|
|
143962
|
+
default:
|
|
143963
|
+
return value;
|
|
143964
|
+
}
|
|
143965
|
+
};
|
|
143966
|
+
const memoryMB = parseMemory(usedStr);
|
|
143967
|
+
const memoryLimit = parseMemory(limitStr);
|
|
143968
|
+
const memPercStr = data.MemPerc || "0%";
|
|
143969
|
+
const memoryPercent = parseFloat(memPercStr.replace("%", "")) || 0;
|
|
143970
|
+
containers.push({
|
|
143971
|
+
id: (data.ID || data.Id || "").slice(0, 12),
|
|
143972
|
+
name: data.Name || data.Names || "unknown",
|
|
143973
|
+
cpuPercent: Math.round(cpuPercent * 10) / 10,
|
|
143974
|
+
memoryMB: Math.round(memoryMB * 10) / 10,
|
|
143975
|
+
memoryLimit: Math.round(memoryLimit * 10) / 10,
|
|
143976
|
+
memoryPercent: Math.round(memoryPercent * 10) / 10
|
|
143977
|
+
});
|
|
143978
|
+
} catch {
|
|
143979
|
+
continue;
|
|
143980
|
+
}
|
|
143981
|
+
}
|
|
143982
|
+
containers.sort((a, b) => b.memoryMB - a.memoryMB);
|
|
143983
|
+
return c.json({ containers, available: true, runtime });
|
|
143984
|
+
} catch (err) {
|
|
143985
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
143986
|
+
return c.json({ error: message }, 500);
|
|
143987
|
+
}
|
|
143988
|
+
});
|
|
143800
143989
|
|
|
143801
143990
|
// server/app.ts
|
|
143802
143991
|
function getDistPath() {
|