@vtstech/pi-status 1.2.2 → 1.2.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.
- package/package.json +10 -6
- package/status.js +392 -13
package/package.json
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vtstech/pi-status",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.3",
|
|
4
4
|
"description": "System monitor / status bar extension for Pi Coding Agent",
|
|
5
5
|
"main": "status.js",
|
|
6
|
-
"keywords": [
|
|
6
|
+
"keywords": [
|
|
7
|
+
"pi-package",
|
|
8
|
+
"pi",
|
|
9
|
+
"pi-coding-agent",
|
|
10
|
+
"pi-extensions"
|
|
11
|
+
],
|
|
7
12
|
"license": "MIT",
|
|
8
13
|
"access": "public",
|
|
9
14
|
"type": "module",
|
|
@@ -13,13 +18,12 @@
|
|
|
13
18
|
"type": "git",
|
|
14
19
|
"url": "https://github.com/VTSTech/pi-coding-agent"
|
|
15
20
|
},
|
|
16
|
-
"dependencies": {
|
|
17
|
-
"@vtstech/pi-shared": "1.2.2"
|
|
18
|
-
},
|
|
19
21
|
"peerDependencies": {
|
|
20
22
|
"@mariozechner/pi-coding-agent": ">=0.66"
|
|
21
23
|
},
|
|
22
24
|
"pi": {
|
|
23
|
-
"extensions": [
|
|
25
|
+
"extensions": [
|
|
26
|
+
"./status.js"
|
|
27
|
+
]
|
|
24
28
|
}
|
|
25
29
|
}
|
package/status.js
CHANGED
|
@@ -1,16 +1,395 @@
|
|
|
1
|
-
//
|
|
2
|
-
import * as
|
|
1
|
+
// extensions/status.ts
|
|
2
|
+
import * as fs3 from "node:fs";
|
|
3
3
|
import { exec } from "node:child_process";
|
|
4
4
|
import { promisify } from "node:util";
|
|
5
|
+
import os4 from "node:os";
|
|
6
|
+
|
|
7
|
+
// shared/ollama.ts
|
|
8
|
+
import * as fs from "node:fs";
|
|
9
|
+
import * as path from "node:path";
|
|
5
10
|
import os from "node:os";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
11
|
+
|
|
12
|
+
// shared/debug.ts
|
|
13
|
+
var DEBUG_ENABLED = process?.env?.PI_EXTENSIONS_DEBUG === "1";
|
|
14
|
+
function debugLog(module, message, ...args) {
|
|
15
|
+
if (!DEBUG_ENABLED) return;
|
|
16
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
17
|
+
console.debug(`[pi-ext:${module}] ${timestamp} ${message}`, ...args);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// shared/ollama.ts
|
|
21
|
+
var MODELS_JSON_PATH = path.join(os.homedir(), ".pi", "agent", "models.json");
|
|
22
|
+
var _modelsJsonCache = null;
|
|
23
|
+
var _ollamaBaseUrlCache = null;
|
|
24
|
+
var CACHE_TTL_MS = 2e3;
|
|
25
|
+
function getOllamaBaseUrl() {
|
|
26
|
+
const now = Date.now();
|
|
27
|
+
if (_ollamaBaseUrlCache && now - _ollamaBaseUrlCache.ts < CACHE_TTL_MS) return _ollamaBaseUrlCache.data;
|
|
28
|
+
try {
|
|
29
|
+
if (fs.existsSync(MODELS_JSON_PATH)) {
|
|
30
|
+
const raw = fs.readFileSync(MODELS_JSON_PATH, "utf-8");
|
|
31
|
+
const config = JSON.parse(raw);
|
|
32
|
+
const baseUrl = config?.providers?.["ollama"]?.baseUrl;
|
|
33
|
+
if (baseUrl) {
|
|
34
|
+
const result = baseUrl.replace(/\/v1\/?$/, "");
|
|
35
|
+
_ollamaBaseUrlCache = { data: result, ts: now };
|
|
36
|
+
return result;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
} catch (err) {
|
|
40
|
+
debugLog("ollama", "failed to parse models.json for base URL", err);
|
|
41
|
+
}
|
|
42
|
+
if (process.env.OLLAMA_HOST) {
|
|
43
|
+
const result = `http://${process.env.OLLAMA_HOST.replace(/^https?:\/\//, "")}`;
|
|
44
|
+
_ollamaBaseUrlCache = { data: result, ts: now };
|
|
45
|
+
return result;
|
|
46
|
+
}
|
|
47
|
+
const fallback = "http://localhost:11434";
|
|
48
|
+
_ollamaBaseUrlCache = { data: fallback, ts: now };
|
|
49
|
+
return fallback;
|
|
50
|
+
}
|
|
51
|
+
function readModelsJson() {
|
|
52
|
+
const now = Date.now();
|
|
53
|
+
if (_modelsJsonCache && now - _modelsJsonCache.ts < CACHE_TTL_MS) return _modelsJsonCache.data;
|
|
54
|
+
try {
|
|
55
|
+
if (fs.existsSync(MODELS_JSON_PATH)) {
|
|
56
|
+
const raw = fs.readFileSync(MODELS_JSON_PATH, "utf-8");
|
|
57
|
+
const data = JSON.parse(raw);
|
|
58
|
+
_modelsJsonCache = { data, ts: now };
|
|
59
|
+
return data;
|
|
60
|
+
}
|
|
61
|
+
} catch (err) {
|
|
62
|
+
debugLog("ollama", "failed to read/parse models.json", err);
|
|
63
|
+
}
|
|
64
|
+
const empty = { providers: {} };
|
|
65
|
+
_modelsJsonCache = { data: empty, ts: now };
|
|
66
|
+
return empty;
|
|
67
|
+
}
|
|
68
|
+
var DEFAULT_RETRY_OPTIONS = {
|
|
69
|
+
maxRetries: 2,
|
|
70
|
+
baseDelayMs: 1e3,
|
|
71
|
+
maxDelayMs: 1e4,
|
|
72
|
+
retryOnTimeout: true,
|
|
73
|
+
retryOnConnectionError: true
|
|
74
|
+
};
|
|
75
|
+
function backoffDelay(attempt, baseDelayMs, maxDelayMs) {
|
|
76
|
+
const delay = Math.min(baseDelayMs * Math.pow(2, attempt), maxDelayMs);
|
|
77
|
+
const jitter = delay * 0.25 * (Math.random() * 2 - 1);
|
|
78
|
+
return Math.max(0, Math.round(delay + jitter));
|
|
79
|
+
}
|
|
80
|
+
var RETRYABLE_ERROR_PATTERNS = [
|
|
81
|
+
"ECONNREFUSED",
|
|
82
|
+
"ECONNRESET",
|
|
83
|
+
"ENOTFOUND",
|
|
84
|
+
"ETIMEDOUT",
|
|
85
|
+
"fetch failed",
|
|
86
|
+
"network error",
|
|
87
|
+
"socket hang up",
|
|
88
|
+
"Empty response"
|
|
89
|
+
];
|
|
90
|
+
function isRetryableError(error, opts) {
|
|
91
|
+
if (error instanceof Error) {
|
|
92
|
+
if (error.name === "AbortError" && opts.retryOnTimeout) return true;
|
|
93
|
+
const msg = error.message;
|
|
94
|
+
if (opts.retryOnConnectionError && RETRYABLE_ERROR_PATTERNS.some((p) => msg.includes(p))) {
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
async function withRetry(fn, options) {
|
|
101
|
+
const opts = { ...DEFAULT_RETRY_OPTIONS, ...options };
|
|
102
|
+
let lastError;
|
|
103
|
+
for (let attempt = 0; attempt <= opts.maxRetries; attempt++) {
|
|
104
|
+
try {
|
|
105
|
+
return await fn();
|
|
106
|
+
} catch (error) {
|
|
107
|
+
lastError = error;
|
|
108
|
+
if (attempt < opts.maxRetries && isRetryableError(error, opts)) {
|
|
109
|
+
const delay = backoffDelay(attempt, opts.baseDelayMs, opts.maxDelayMs);
|
|
110
|
+
debugLog("ollama", `Retry ${attempt + 1}/${opts.maxRetries} after ${delay}ms: ${error instanceof Error ? error.message : String(error)}`);
|
|
111
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
throw error;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
throw lastError;
|
|
118
|
+
}
|
|
119
|
+
async function fetchModelContextLength(baseUrl, modelName) {
|
|
120
|
+
return withRetry(async () => {
|
|
121
|
+
try {
|
|
122
|
+
const res = await fetch(`${baseUrl}/api/show`, {
|
|
123
|
+
method: "POST",
|
|
124
|
+
headers: { "Content-Type": "application/json" },
|
|
125
|
+
body: JSON.stringify({ name: modelName }),
|
|
126
|
+
signal: AbortSignal.timeout(3e4)
|
|
127
|
+
});
|
|
128
|
+
if (!res.ok) return void 0;
|
|
129
|
+
const data = await res.json();
|
|
130
|
+
for (const key of Object.keys(data?.model_info ?? {})) {
|
|
131
|
+
if (key.endsWith(".context_length")) {
|
|
132
|
+
const val = data.model_info[key];
|
|
133
|
+
if (typeof val === "number") return val;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
const numCtx = data?.model_info?.["num_ctx"];
|
|
137
|
+
if (typeof numCtx === "number") return numCtx;
|
|
138
|
+
} catch (err) {
|
|
139
|
+
debugLog("ollama", `failed to fetch context length for ${modelName}`, err);
|
|
140
|
+
return void 0;
|
|
141
|
+
}
|
|
142
|
+
return void 0;
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
function isLocalProvider(baseUrl, providerName) {
|
|
146
|
+
if (providerName === "ollama") return true;
|
|
147
|
+
const url = baseUrl || "";
|
|
148
|
+
return url.includes("localhost") || url.includes("127.0.0.1") || url.includes("0.0.0.0");
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// shared/format.ts
|
|
152
|
+
function fmtBytes(b) {
|
|
153
|
+
if (b === 0) return "0B";
|
|
154
|
+
if (b < 1024) return `${b}B`;
|
|
155
|
+
if (b >= 1073741824) return `${(b / 1073741824).toFixed(1)}G`;
|
|
156
|
+
if (b >= 1048576) return `${(b / 1048576).toFixed(0)}M`;
|
|
157
|
+
return `${(b / 1024).toFixed(0)}K`;
|
|
158
|
+
}
|
|
159
|
+
function fmtDur(ms) {
|
|
160
|
+
if (ms < 1e3) return `${Math.round(ms)}ms`;
|
|
161
|
+
if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}s`;
|
|
162
|
+
return `${Math.floor(ms / 6e4)}m${Math.round(ms % 6e4 / 1e3)}s`;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// shared/security.ts
|
|
166
|
+
import * as fs2 from "node:fs";
|
|
167
|
+
import * as path3 from "node:path";
|
|
168
|
+
import os3 from "node:os";
|
|
169
|
+
|
|
170
|
+
// shared/config-io.ts
|
|
171
|
+
import * as path2 from "path";
|
|
172
|
+
import os2 from "os";
|
|
173
|
+
var PI_AGENT_DIR = path2.join(os2.homedir(), ".pi", "agent");
|
|
174
|
+
var SETTINGS_PATH = path2.join(PI_AGENT_DIR, "settings.json");
|
|
175
|
+
var SECURITY_PATH = path2.join(PI_AGENT_DIR, "security.json");
|
|
176
|
+
var REACT_MODE_PATH = path2.join(PI_AGENT_DIR, "react-mode.json");
|
|
177
|
+
var MODEL_TEST_CONFIG_PATH = path2.join(PI_AGENT_DIR, "model-test-config.json");
|
|
178
|
+
|
|
179
|
+
// shared/security.ts
|
|
180
|
+
var SECURITY_CONFIG_PATH = SECURITY_PATH;
|
|
181
|
+
var securityModeCache = null;
|
|
182
|
+
var securityModeCacheTime = 0;
|
|
183
|
+
var SECURITY_CACHE_DURATION_MS = 3e4;
|
|
184
|
+
function getSecurityMode() {
|
|
185
|
+
const now = Date.now();
|
|
186
|
+
if (securityModeCache && now - securityModeCacheTime < SECURITY_CACHE_DURATION_MS) {
|
|
187
|
+
return securityModeCache;
|
|
188
|
+
}
|
|
189
|
+
try {
|
|
190
|
+
if (!fs2.existsSync(SECURITY_CONFIG_PATH)) {
|
|
191
|
+
securityModeCache = "max";
|
|
192
|
+
securityModeCacheTime = now;
|
|
193
|
+
return "max";
|
|
194
|
+
}
|
|
195
|
+
const raw = fs2.readFileSync(SECURITY_CONFIG_PATH, "utf-8");
|
|
196
|
+
const config = JSON.parse(raw);
|
|
197
|
+
if (config.mode === "basic" || config.mode === "max" || config.mode === "off") {
|
|
198
|
+
securityModeCache = config.mode;
|
|
199
|
+
securityModeCacheTime = now;
|
|
200
|
+
return config.mode;
|
|
201
|
+
}
|
|
202
|
+
securityModeCache = "max";
|
|
203
|
+
securityModeCacheTime = now;
|
|
204
|
+
return "max";
|
|
205
|
+
} catch (err) {
|
|
206
|
+
debugLog("security", `failed to read security config at ${SECURITY_CONFIG_PATH}`, err);
|
|
207
|
+
securityModeCache = "max";
|
|
208
|
+
securityModeCacheTime = now;
|
|
209
|
+
return "max";
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
var CRITICAL_COMMANDS = /* @__PURE__ */ new Set([
|
|
213
|
+
// Filesystem destruction (irrecoverable)
|
|
214
|
+
"mkfs",
|
|
215
|
+
"dd",
|
|
216
|
+
"shred",
|
|
217
|
+
"wipe",
|
|
218
|
+
"srm",
|
|
219
|
+
"format",
|
|
220
|
+
"fdisk",
|
|
221
|
+
// Privilege escalation (non-sudo)
|
|
222
|
+
"su",
|
|
223
|
+
"doas",
|
|
224
|
+
"pkexec",
|
|
225
|
+
"gksudo",
|
|
226
|
+
"kdesu",
|
|
227
|
+
// Network attack tools
|
|
228
|
+
"nmap",
|
|
229
|
+
"nc",
|
|
230
|
+
"netcat",
|
|
231
|
+
"telnet",
|
|
232
|
+
// Remote access
|
|
233
|
+
"ssh",
|
|
234
|
+
"scp",
|
|
235
|
+
"sftp",
|
|
236
|
+
"rsync",
|
|
237
|
+
// Process killing
|
|
238
|
+
"kill",
|
|
239
|
+
"killall",
|
|
240
|
+
"pkill",
|
|
241
|
+
"xkill",
|
|
242
|
+
// User management
|
|
243
|
+
"useradd",
|
|
244
|
+
"userdel",
|
|
245
|
+
"usermod",
|
|
246
|
+
"passwd",
|
|
247
|
+
"adduser",
|
|
248
|
+
"deluser",
|
|
249
|
+
// Dangerous shell features
|
|
250
|
+
"exec",
|
|
251
|
+
"eval",
|
|
252
|
+
"source",
|
|
253
|
+
".",
|
|
254
|
+
"alias",
|
|
255
|
+
// Filesystem control
|
|
256
|
+
"mount",
|
|
257
|
+
"umount",
|
|
258
|
+
"chattr",
|
|
259
|
+
"lsattr",
|
|
260
|
+
// Permission modification
|
|
261
|
+
"chown",
|
|
262
|
+
"chmod"
|
|
263
|
+
]);
|
|
264
|
+
var EXTENDED_COMMANDS = /* @__PURE__ */ new Set([
|
|
265
|
+
// File deletion
|
|
266
|
+
"rm",
|
|
267
|
+
"rmdir",
|
|
268
|
+
"del",
|
|
269
|
+
// Privilege escalation
|
|
270
|
+
"sudo",
|
|
271
|
+
// Download tools
|
|
272
|
+
"wget",
|
|
273
|
+
"curl",
|
|
274
|
+
// Package management
|
|
275
|
+
"apt",
|
|
276
|
+
"apt-get",
|
|
277
|
+
"yum",
|
|
278
|
+
"dnf",
|
|
279
|
+
"pacman",
|
|
280
|
+
"pip",
|
|
281
|
+
"npm",
|
|
282
|
+
"yarn",
|
|
283
|
+
"cargo",
|
|
284
|
+
// System service control
|
|
285
|
+
"systemctl",
|
|
286
|
+
"service",
|
|
287
|
+
// Interactive editors (shell escape risk)
|
|
288
|
+
"vi",
|
|
289
|
+
"vim",
|
|
290
|
+
"nano",
|
|
291
|
+
"emacs",
|
|
292
|
+
"less",
|
|
293
|
+
"more",
|
|
294
|
+
"man",
|
|
295
|
+
// Version control
|
|
296
|
+
"git"
|
|
297
|
+
]);
|
|
298
|
+
var BLOCKED_COMMANDS = /* @__PURE__ */ new Set([
|
|
299
|
+
...CRITICAL_COMMANDS,
|
|
300
|
+
...EXTENDED_COMMANDS
|
|
301
|
+
]);
|
|
302
|
+
var BLOCKED_URL_ALWAYS = /* @__PURE__ */ new Set([
|
|
303
|
+
// Cloud metadata endpoints
|
|
304
|
+
"169.254.169.254",
|
|
305
|
+
// RFC1918 private ranges
|
|
306
|
+
"10.",
|
|
307
|
+
"192.168.",
|
|
308
|
+
"172.16.",
|
|
309
|
+
"172.17.",
|
|
310
|
+
"172.18.",
|
|
311
|
+
"172.19.",
|
|
312
|
+
"172.20.",
|
|
313
|
+
"172.21.",
|
|
314
|
+
"172.22.",
|
|
315
|
+
"172.23.",
|
|
316
|
+
"172.24.",
|
|
317
|
+
"172.25.",
|
|
318
|
+
"172.26.",
|
|
319
|
+
"172.27.",
|
|
320
|
+
"172.28.",
|
|
321
|
+
"172.29.",
|
|
322
|
+
"172.30.",
|
|
323
|
+
"172.31.",
|
|
324
|
+
// IPv6-mapped IPv4 cloud metadata (always blocked)
|
|
325
|
+
"::ffff:169.254.169.254",
|
|
326
|
+
// Internal service patterns
|
|
327
|
+
"internal.",
|
|
328
|
+
"private.",
|
|
329
|
+
"intranet."
|
|
330
|
+
]);
|
|
331
|
+
var BLOCKED_URL_MAX_ONLY = /* @__PURE__ */ new Set([
|
|
332
|
+
// Loopback addresses (full 127.0.0.0/8 range)
|
|
333
|
+
"localhost",
|
|
334
|
+
"127.",
|
|
335
|
+
"0.0.0.0",
|
|
336
|
+
"::1",
|
|
337
|
+
"::ffff:127.0.0.1",
|
|
338
|
+
"::ffff:0.0.0.0",
|
|
339
|
+
// IPv6-mapped IPv4 private ranges (always blocked in max mode)
|
|
340
|
+
"::ffff:10.",
|
|
341
|
+
"::ffff:192.168.",
|
|
342
|
+
"::ffff:172.16.",
|
|
343
|
+
"::ffff:172.17.",
|
|
344
|
+
"::ffff:172.18.",
|
|
345
|
+
"::ffff:172.19.",
|
|
346
|
+
"::ffff:172.20.",
|
|
347
|
+
"::ffff:172.21.",
|
|
348
|
+
"::ffff:172.22.",
|
|
349
|
+
"::ffff:172.23.",
|
|
350
|
+
"::ffff:172.24.",
|
|
351
|
+
"::ffff:172.25.",
|
|
352
|
+
"::ffff:172.26.",
|
|
353
|
+
"::ffff:172.27.",
|
|
354
|
+
"::ffff:172.28.",
|
|
355
|
+
"::ffff:172.29.",
|
|
356
|
+
"::ffff:172.30.",
|
|
357
|
+
"::ffff:172.31.",
|
|
358
|
+
// Local/internal patterns
|
|
359
|
+
"local."
|
|
360
|
+
]);
|
|
361
|
+
var BLOCKED_URL_PATTERNS = /* @__PURE__ */ new Set([
|
|
362
|
+
...BLOCKED_URL_ALWAYS,
|
|
363
|
+
...BLOCKED_URL_MAX_ONLY
|
|
364
|
+
]);
|
|
365
|
+
var AUDIT_DIR = path3.join(os3.homedir(), ".pi", "agent");
|
|
366
|
+
var AUDIT_LOG_PATH = path3.join(AUDIT_DIR, "audit.log");
|
|
367
|
+
var _auditBuffer = [];
|
|
368
|
+
function flushAuditBuffer() {
|
|
369
|
+
if (_auditBuffer.length === 0) return;
|
|
370
|
+
try {
|
|
371
|
+
if (!fs2.existsSync(AUDIT_DIR)) {
|
|
372
|
+
fs2.mkdirSync(AUDIT_DIR, { recursive: true });
|
|
373
|
+
}
|
|
374
|
+
const batch = _auditBuffer.join("");
|
|
375
|
+
fs2.appendFileSync(AUDIT_LOG_PATH, batch, "utf-8");
|
|
376
|
+
} catch (err) {
|
|
377
|
+
debugLog("security", "audit buffer flush failure", err);
|
|
378
|
+
}
|
|
379
|
+
_auditBuffer = [];
|
|
380
|
+
}
|
|
381
|
+
process.on("exit", () => {
|
|
382
|
+
flushAuditBuffer();
|
|
383
|
+
});
|
|
384
|
+
process.on("SIGTERM", () => {
|
|
385
|
+
flushAuditBuffer();
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
// extensions/status.ts
|
|
10
389
|
var execAsync = promisify(exec);
|
|
11
390
|
var STATUS_UPDATE_INTERVAL_MS = 5e3;
|
|
12
391
|
var TOOL_TIMER_INTERVAL_MS = 1e3;
|
|
13
|
-
function
|
|
392
|
+
function status_default(pi) {
|
|
14
393
|
let lastResponseTime = null;
|
|
15
394
|
let agentStartTime = null;
|
|
16
395
|
let updateInterval = null;
|
|
@@ -38,7 +417,7 @@ function status_temp_default(pi) {
|
|
|
38
417
|
let activeToolStart = 0;
|
|
39
418
|
let blockedCount = 0;
|
|
40
419
|
function getCpuSnapshot() {
|
|
41
|
-
return
|
|
420
|
+
return os4.cpus().map((c) => ({
|
|
42
421
|
user: c.times.user,
|
|
43
422
|
nice: c.times.nice,
|
|
44
423
|
sys: c.times.sys,
|
|
@@ -46,7 +425,7 @@ function status_temp_default(pi) {
|
|
|
46
425
|
}));
|
|
47
426
|
}
|
|
48
427
|
function getCpuUsage() {
|
|
49
|
-
const cpus =
|
|
428
|
+
const cpus = os4.cpus();
|
|
50
429
|
const n = cpus.length;
|
|
51
430
|
let totalUsed = 0, totalDelta = 0;
|
|
52
431
|
for (let i = 0; i < n; i++) {
|
|
@@ -64,8 +443,8 @@ function status_temp_default(pi) {
|
|
|
64
443
|
return totalDelta > 0 ? totalUsed / totalDelta * 100 : 0;
|
|
65
444
|
}
|
|
66
445
|
function getMem() {
|
|
67
|
-
const total =
|
|
68
|
-
const used = total -
|
|
446
|
+
const total = os4.totalmem();
|
|
447
|
+
const used = total - os4.freemem();
|
|
69
448
|
return { used, total };
|
|
70
449
|
}
|
|
71
450
|
async function getSwap() {
|
|
@@ -74,7 +453,7 @@ function status_temp_default(pi) {
|
|
|
74
453
|
return null;
|
|
75
454
|
}
|
|
76
455
|
try {
|
|
77
|
-
const out = await
|
|
456
|
+
const out = await fs3.promises.readFile("/proc/meminfo", "utf-8");
|
|
78
457
|
const swapTotal2 = Number(out.match(/SwapTotal:\s+(\d+)/)?.[1]) * 1024;
|
|
79
458
|
const swapFree = Number(out.match(/SwapFree:\s+(\d+)/)?.[1]) * 1024;
|
|
80
459
|
if (swapTotal2 > 0) return { used: swapTotal2 - swapFree, total: swapTotal2 };
|
|
@@ -347,5 +726,5 @@ function status_temp_default(pi) {
|
|
|
347
726
|
});
|
|
348
727
|
}
|
|
349
728
|
export {
|
|
350
|
-
|
|
729
|
+
status_default as default
|
|
351
730
|
};
|