@vtstech/pi-status 1.0.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 +24 -0
- package/status.js +327 -0
package/package.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vtstech/pi-status",
|
|
3
|
+
"version": "1.0.3",
|
|
4
|
+
"description": "System monitor / status bar extension for Pi Coding Agent",
|
|
5
|
+
"main": "status.js",
|
|
6
|
+
"keywords": ["pi-package", "pi-extensions"],
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"access": "public",
|
|
9
|
+
"author": "VTSTech",
|
|
10
|
+
"homepage": "https://www.vts-tech.org",
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "https://github.com/VTSTech/pi-coding-agent"
|
|
14
|
+
},
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"@vtstech/pi-shared": "1.0.3"
|
|
17
|
+
},
|
|
18
|
+
"peerDependencies": {
|
|
19
|
+
"@mariozechner/pi-coding-agent": ">=0.66"
|
|
20
|
+
},
|
|
21
|
+
"pi": {
|
|
22
|
+
"extensions": ["./status.js"]
|
|
23
|
+
}
|
|
24
|
+
}
|
package/status.js
ADDED
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
20
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
21
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
22
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
23
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
24
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
25
|
+
mod
|
|
26
|
+
));
|
|
27
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
28
|
+
|
|
29
|
+
// .build-npm/status/status.temp.ts
|
|
30
|
+
var status_temp_exports = {};
|
|
31
|
+
__export(status_temp_exports, {
|
|
32
|
+
default: () => status_temp_default
|
|
33
|
+
});
|
|
34
|
+
module.exports = __toCommonJS(status_temp_exports);
|
|
35
|
+
var import_node_os = __toESM(require("node:os"));
|
|
36
|
+
var import_node_child_process = require("node:child_process");
|
|
37
|
+
var import_ollama = require("@vtstech/pi-shared/ollama");
|
|
38
|
+
var import_format = require("@vtstech/pi-shared/format");
|
|
39
|
+
var import_security = require("@vtstech/pi-shared/security");
|
|
40
|
+
function status_temp_default(pi) {
|
|
41
|
+
let lastResponseTime = null;
|
|
42
|
+
let agentStartTime = null;
|
|
43
|
+
let updateInterval = null;
|
|
44
|
+
let currentCtx = null;
|
|
45
|
+
let ctxUi = null;
|
|
46
|
+
let prevCpuInfo = getCpuSnapshot();
|
|
47
|
+
let lastPayload = null;
|
|
48
|
+
let tuiRef = null;
|
|
49
|
+
let gitBranchCache = "";
|
|
50
|
+
let cpuUsage = 0;
|
|
51
|
+
let memUsed = 0;
|
|
52
|
+
let memTotal = 0;
|
|
53
|
+
let swapUsed = 0;
|
|
54
|
+
let swapTotal = 0;
|
|
55
|
+
let hasSwap = false;
|
|
56
|
+
let ollamaLoaded = "";
|
|
57
|
+
let footerModel = "";
|
|
58
|
+
let footerThinking = "";
|
|
59
|
+
let footerCtxPct = "";
|
|
60
|
+
let securityFlashTool = "";
|
|
61
|
+
let securityFlashUntil = 0;
|
|
62
|
+
let activeTool = "";
|
|
63
|
+
let activeToolStart = 0;
|
|
64
|
+
let blockedCount = 0;
|
|
65
|
+
function getCpuSnapshot() {
|
|
66
|
+
return import_node_os.default.cpus().map((c) => ({
|
|
67
|
+
user: c.times.user,
|
|
68
|
+
nice: c.times.nice,
|
|
69
|
+
sys: c.times.sys,
|
|
70
|
+
idle: c.times.idle
|
|
71
|
+
}));
|
|
72
|
+
}
|
|
73
|
+
function getCpuUsage() {
|
|
74
|
+
const cpus = import_node_os.default.cpus();
|
|
75
|
+
const n = cpus.length;
|
|
76
|
+
let totalUsed = 0, totalDelta = 0;
|
|
77
|
+
for (let i = 0; i < n; i++) {
|
|
78
|
+
const prev = prevCpuInfo[i];
|
|
79
|
+
const curr = cpus[i].times;
|
|
80
|
+
const prevTotal = prev.user + prev.nice + prev.sys + prev.idle;
|
|
81
|
+
const currTotal = curr.user + curr.nice + curr.sys + curr.idle;
|
|
82
|
+
const d = currTotal - prevTotal;
|
|
83
|
+
if (d > 0) {
|
|
84
|
+
totalUsed += d - (curr.idle - prev.idle);
|
|
85
|
+
totalDelta += d;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
prevCpuInfo = getCpuSnapshot();
|
|
89
|
+
return totalDelta > 0 ? totalUsed / totalDelta * 100 : 0;
|
|
90
|
+
}
|
|
91
|
+
function getMem() {
|
|
92
|
+
const total = import_node_os.default.totalmem();
|
|
93
|
+
const used = total - import_node_os.default.freemem();
|
|
94
|
+
return { used, total };
|
|
95
|
+
}
|
|
96
|
+
function getSwap() {
|
|
97
|
+
try {
|
|
98
|
+
const out = (0, import_node_child_process.execSync)("cat /proc/meminfo", { encoding: "utf-8", timeout: 3e3 });
|
|
99
|
+
const swapTotal2 = Number(out.match(/SwapTotal:\s+(\d+)/)?.[1]) * 1024;
|
|
100
|
+
const swapFree = Number(out.match(/SwapFree:\s+(\d+)/)?.[1]) * 1024;
|
|
101
|
+
if (swapTotal2 > 0) return { used: swapTotal2 - swapFree, total: swapTotal2 };
|
|
102
|
+
} catch {
|
|
103
|
+
}
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
let ollamaLoadedCache = "";
|
|
107
|
+
let ollamaLoadedLastCheck = 0;
|
|
108
|
+
const OLLAMA_LOADED_INTERVAL = 15e3;
|
|
109
|
+
function getOllamaLoadedModel() {
|
|
110
|
+
const now = Date.now();
|
|
111
|
+
if (now - ollamaLoadedLastCheck < OLLAMA_LOADED_INTERVAL) return ollamaLoadedCache;
|
|
112
|
+
ollamaLoadedLastCheck = now;
|
|
113
|
+
try {
|
|
114
|
+
const ollamaBase = (0, import_ollama.getOllamaBaseUrl)();
|
|
115
|
+
const out = (0, import_node_child_process.execSync)(`curl -s "${ollamaBase}/api/ps"`, { encoding: "utf-8", timeout: 5e3 });
|
|
116
|
+
if (out.trim()) {
|
|
117
|
+
const data = JSON.parse(out.trim());
|
|
118
|
+
const models = data?.models || [];
|
|
119
|
+
if (Array.isArray(models) && models.length > 0) {
|
|
120
|
+
ollamaLoadedCache = models[0].name || models[0].model || "unknown";
|
|
121
|
+
return ollamaLoadedCache;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
} catch {
|
|
125
|
+
}
|
|
126
|
+
ollamaLoadedCache = "";
|
|
127
|
+
return "";
|
|
128
|
+
}
|
|
129
|
+
function extractParams(payload) {
|
|
130
|
+
const params = [];
|
|
131
|
+
if (payload.temperature !== void 0) params.push(`temp:${payload.temperature}`);
|
|
132
|
+
if (payload.top_p !== void 0) params.push(`top_p:${payload.top_p}`);
|
|
133
|
+
if (payload.top_k !== void 0) params.push(`top_k:${payload.top_k}`);
|
|
134
|
+
if (payload.max_completion_tokens !== void 0) params.push(`max:${payload.max_completion_tokens}`);
|
|
135
|
+
else if (payload.max_tokens !== void 0) params.push(`max:${payload.max_tokens}`);
|
|
136
|
+
if (payload.num_predict !== void 0) params.push(`predict:${payload.num_predict}`);
|
|
137
|
+
if (payload.num_ctx !== void 0) params.push(`ctx:${payload.num_ctx}`);
|
|
138
|
+
if (payload.reasoning_effort !== void 0) params.push(`think:${payload.reasoning_effort}`);
|
|
139
|
+
return params;
|
|
140
|
+
}
|
|
141
|
+
function getPwd() {
|
|
142
|
+
const cwd = process.cwd();
|
|
143
|
+
if (cwd.startsWith(import_node_os.default.homedir())) return "~" + cwd.slice(import_node_os.default.homedir().length);
|
|
144
|
+
return cwd;
|
|
145
|
+
}
|
|
146
|
+
function getGitBranch() {
|
|
147
|
+
if (gitBranchCache) return gitBranchCache;
|
|
148
|
+
try {
|
|
149
|
+
const branch = (0, import_node_child_process.execSync)("git rev-parse --abbrev-ref HEAD 2>/dev/null", {
|
|
150
|
+
encoding: "utf-8",
|
|
151
|
+
timeout: 3e3
|
|
152
|
+
}).trim();
|
|
153
|
+
if (branch) gitBranchCache = branch;
|
|
154
|
+
} catch {
|
|
155
|
+
}
|
|
156
|
+
return gitBranchCache;
|
|
157
|
+
}
|
|
158
|
+
function refreshBlockedCount() {
|
|
159
|
+
try {
|
|
160
|
+
const entries = (0, import_security.readRecentAuditEntries)(50);
|
|
161
|
+
blockedCount = 0;
|
|
162
|
+
for (const entry of entries) {
|
|
163
|
+
if (entry.blocked === true || entry.safe === false || entry.action === "block") {
|
|
164
|
+
blockedCount++;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
} catch {
|
|
168
|
+
blockedCount = 0;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
function updateMetrics() {
|
|
172
|
+
cpuUsage = getCpuUsage();
|
|
173
|
+
const mem = getMem();
|
|
174
|
+
memUsed = mem.used;
|
|
175
|
+
memTotal = mem.total;
|
|
176
|
+
const swap = getSwap();
|
|
177
|
+
if (swap) {
|
|
178
|
+
swapUsed = swap.used;
|
|
179
|
+
swapTotal = swap.total;
|
|
180
|
+
hasSwap = true;
|
|
181
|
+
} else {
|
|
182
|
+
hasSwap = false;
|
|
183
|
+
}
|
|
184
|
+
ollamaLoaded = getOllamaLoadedModel();
|
|
185
|
+
if (currentCtx) {
|
|
186
|
+
footerModel = currentCtx.model?.id || "";
|
|
187
|
+
footerThinking = pi.getThinkingLevel?.() ?? "";
|
|
188
|
+
const usage = currentCtx.getContextUsage?.();
|
|
189
|
+
if (usage && usage.contextWindow > 0) {
|
|
190
|
+
const pct = (usage.tokens / usage.contextWindow * 100).toFixed(1);
|
|
191
|
+
footerCtxPct = `${pct}%/${(usage.contextWindow / 1e3).toFixed(0)}k`;
|
|
192
|
+
} else {
|
|
193
|
+
footerCtxPct = "";
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
refreshBlockedCount();
|
|
197
|
+
}
|
|
198
|
+
pi.on("session_start", async (_event, ctx) => {
|
|
199
|
+
currentCtx = ctx;
|
|
200
|
+
ctxUi = ctx.ui;
|
|
201
|
+
prevCpuInfo = getCpuSnapshot();
|
|
202
|
+
updateMetrics();
|
|
203
|
+
ctx.ui.setFooter((tui, theme, footerData) => {
|
|
204
|
+
tuiRef = tui;
|
|
205
|
+
const dim = (s) => theme?.fg?.("dim", s) ?? s;
|
|
206
|
+
const red = (s) => theme?.fg?.("red", s) ?? s;
|
|
207
|
+
const yellow = (s) => theme?.fg?.("yellow", s) ?? s;
|
|
208
|
+
const sep = dim(" \xB7 ");
|
|
209
|
+
return {
|
|
210
|
+
render(width) {
|
|
211
|
+
const lines = [];
|
|
212
|
+
const parts = [];
|
|
213
|
+
parts.push(getPwd());
|
|
214
|
+
let branch = "";
|
|
215
|
+
try {
|
|
216
|
+
branch = footerData?.getGitBranch?.() || "";
|
|
217
|
+
} catch {
|
|
218
|
+
}
|
|
219
|
+
if (!branch) branch = getGitBranch();
|
|
220
|
+
if (branch) parts.push(dim(branch));
|
|
221
|
+
if (footerModel) parts.push(dim(footerModel));
|
|
222
|
+
if (footerThinking && footerThinking !== "off") parts.push(dim(footerThinking));
|
|
223
|
+
if (footerCtxPct) parts.push(footerCtxPct);
|
|
224
|
+
parts.push(dim(`CPU ${cpuUsage.toFixed(0)}%`));
|
|
225
|
+
parts.push(`RAM ${(0, import_format.fmtBytes)(memUsed)}/${(0, import_format.fmtBytes)(memTotal)}`);
|
|
226
|
+
if (hasSwap && swapUsed > 0) {
|
|
227
|
+
parts.push(`Swap ${(0, import_format.fmtBytes)(swapUsed)}/${(0, import_format.fmtBytes)(swapTotal)}`);
|
|
228
|
+
}
|
|
229
|
+
if (ollamaLoaded) parts.push(`${ollamaLoaded}`);
|
|
230
|
+
if (lastResponseTime !== null) parts.push(`Resp ${(0, import_format.fmtDur)(lastResponseTime)}`);
|
|
231
|
+
if (lastPayload) {
|
|
232
|
+
const params = extractParams(lastPayload);
|
|
233
|
+
if (params.length > 0) parts.push(...params.map((p) => dim(p)));
|
|
234
|
+
}
|
|
235
|
+
const now = Date.now();
|
|
236
|
+
if (securityFlashTool && now < securityFlashUntil) {
|
|
237
|
+
parts.push(red(`BLOCKED:${securityFlashTool}`));
|
|
238
|
+
}
|
|
239
|
+
if (blockedCount > 0) {
|
|
240
|
+
parts.push(red(`SEC:${blockedCount}`));
|
|
241
|
+
}
|
|
242
|
+
let line = parts.join(sep);
|
|
243
|
+
const visible = line.replace(/\x1b\[[0-9;]*m/g, "");
|
|
244
|
+
if (visible.length > width) {
|
|
245
|
+
let vis = 0, cut = 0;
|
|
246
|
+
for (let i = 0; i < line.length && vis < width - 3; i++) {
|
|
247
|
+
if (line[i] === "\x1B") {
|
|
248
|
+
while (i < line.length && line[i] !== "m") i++;
|
|
249
|
+
} else {
|
|
250
|
+
vis++;
|
|
251
|
+
}
|
|
252
|
+
cut = i + 1;
|
|
253
|
+
}
|
|
254
|
+
line = line.slice(0, cut) + dim("...");
|
|
255
|
+
}
|
|
256
|
+
lines.push(line);
|
|
257
|
+
if (activeTool && activeToolStart > 0) {
|
|
258
|
+
const elapsed = performance.now() - activeToolStart;
|
|
259
|
+
lines.push(`${yellow("\u23F3")} ${activeTool}: ${(0, import_format.fmtDur)(elapsed)}`);
|
|
260
|
+
}
|
|
261
|
+
return lines;
|
|
262
|
+
},
|
|
263
|
+
invalidate() {
|
|
264
|
+
},
|
|
265
|
+
dispose() {
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
});
|
|
269
|
+
if (updateInterval) clearInterval(updateInterval);
|
|
270
|
+
updateInterval = setInterval(() => {
|
|
271
|
+
updateMetrics();
|
|
272
|
+
if (tuiRef) tuiRef.requestRender();
|
|
273
|
+
}, 3e3);
|
|
274
|
+
});
|
|
275
|
+
pi.on("session_shutdown", async () => {
|
|
276
|
+
if (updateInterval) clearInterval(updateInterval);
|
|
277
|
+
updateInterval = null;
|
|
278
|
+
tuiRef = null;
|
|
279
|
+
if (ctxUi) {
|
|
280
|
+
ctxUi.setFooter(void 0);
|
|
281
|
+
ctxUi = null;
|
|
282
|
+
}
|
|
283
|
+
currentCtx = null;
|
|
284
|
+
securityFlashTool = "";
|
|
285
|
+
securityFlashUntil = 0;
|
|
286
|
+
activeTool = "";
|
|
287
|
+
activeToolStart = 0;
|
|
288
|
+
blockedCount = 0;
|
|
289
|
+
});
|
|
290
|
+
pi.on("before_provider_request", (event) => {
|
|
291
|
+
lastPayload = event.payload;
|
|
292
|
+
});
|
|
293
|
+
pi.on("agent_start", async () => {
|
|
294
|
+
agentStartTime = performance.now();
|
|
295
|
+
});
|
|
296
|
+
pi.on("agent_end", async () => {
|
|
297
|
+
if (agentStartTime !== null) {
|
|
298
|
+
lastResponseTime = performance.now() - agentStartTime;
|
|
299
|
+
agentStartTime = null;
|
|
300
|
+
}
|
|
301
|
+
activeTool = "";
|
|
302
|
+
activeToolStart = 0;
|
|
303
|
+
updateMetrics();
|
|
304
|
+
if (tuiRef) tuiRef.requestRender();
|
|
305
|
+
});
|
|
306
|
+
pi.on("tool_call", (event) => {
|
|
307
|
+
if (!event) return;
|
|
308
|
+
const isBlocked = event.blocked === true || event.blocked === "true" || event.result?.blocked === true || event.error?.includes("blocked");
|
|
309
|
+
if (isBlocked) {
|
|
310
|
+
securityFlashTool = event.tool ?? event.name ?? "unknown";
|
|
311
|
+
securityFlashUntil = Date.now() + 3e3;
|
|
312
|
+
refreshBlockedCount();
|
|
313
|
+
if (tuiRef) tuiRef.requestRender();
|
|
314
|
+
}
|
|
315
|
+
});
|
|
316
|
+
pi.on("tool_execution_start", (event) => {
|
|
317
|
+
if (!event) return;
|
|
318
|
+
activeTool = event.tool ?? event.name ?? "tool";
|
|
319
|
+
activeToolStart = performance.now();
|
|
320
|
+
if (tuiRef) tuiRef.requestRender();
|
|
321
|
+
});
|
|
322
|
+
pi.on("tool_execution_end", () => {
|
|
323
|
+
activeTool = "";
|
|
324
|
+
activeToolStart = 0;
|
|
325
|
+
if (tuiRef) tuiRef.requestRender();
|
|
326
|
+
});
|
|
327
|
+
}
|