@vtstech/pi-status 1.0.6 → 1.0.8
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 +2 -2
- package/status.js +117 -50
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vtstech/pi-status",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.8",
|
|
4
4
|
"description": "System monitor / status bar extension for Pi Coding Agent",
|
|
5
5
|
"main": "status.js",
|
|
6
6
|
"keywords": ["pi-extensions"],
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"url": "https://github.com/VTSTech/pi-coding-agent"
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@vtstech/pi-shared": "1.0.
|
|
17
|
+
"@vtstech/pi-shared": "1.0.8"
|
|
18
18
|
},
|
|
19
19
|
"peerDependencies": {
|
|
20
20
|
"@mariozechner/pi-coding-agent": ">=0.66"
|
package/status.js
CHANGED
|
@@ -26,7 +26,11 @@ function status_temp_default(pi) {
|
|
|
26
26
|
let footerModel = "";
|
|
27
27
|
let footerThinking = "";
|
|
28
28
|
let footerCtxPct = "";
|
|
29
|
+
let footerNativeCtx = "";
|
|
30
|
+
let nativeCtxModel = "";
|
|
29
31
|
let isLocalProvider = true;
|
|
32
|
+
let lastUpstream = 0;
|
|
33
|
+
let lastDownstream = 0;
|
|
30
34
|
let securityFlashTool = "";
|
|
31
35
|
let securityFlashUntil = 0;
|
|
32
36
|
let activeTool = "";
|
|
@@ -77,25 +81,61 @@ function status_temp_default(pi) {
|
|
|
77
81
|
let ollamaLoadedLastCheck = 0;
|
|
78
82
|
const OLLAMA_LOADED_INTERVAL = 15e3;
|
|
79
83
|
function detectLocalProvider(modelsJson) {
|
|
84
|
+
const isLocalUrl = (url) => url.includes("localhost") || url.includes("127.0.0.1") || url.includes("0.0.0.0");
|
|
80
85
|
try {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
const url = provider.baseUrl || "";
|
|
91
|
-
if (url.includes("localhost") || url.includes("127.0.0.1") || url.includes("0.0.0.0") || name === "ollama") {
|
|
92
|
-
return true;
|
|
86
|
+
const ctxUrl = currentCtx?.provider?.baseUrl || currentCtx?.provider?.url || "";
|
|
87
|
+
if (ctxUrl) return isLocalUrl(ctxUrl);
|
|
88
|
+
const modelId = footerModel || "";
|
|
89
|
+
if (modelsJson && modelId) {
|
|
90
|
+
for (const provider of Object.values(modelsJson.providers || {})) {
|
|
91
|
+
const url = provider.baseUrl || "";
|
|
92
|
+
if ((provider.models || []).some((m) => m.id === modelId)) {
|
|
93
|
+
return isLocalUrl(url);
|
|
94
|
+
}
|
|
93
95
|
}
|
|
94
96
|
}
|
|
95
97
|
} catch {
|
|
96
98
|
}
|
|
97
99
|
return false;
|
|
98
100
|
}
|
|
101
|
+
let nativeCtxPromise = null;
|
|
102
|
+
function getNativeModelCtx(modelId) {
|
|
103
|
+
if (!modelId) return "";
|
|
104
|
+
if (modelId === nativeCtxModel && footerNativeCtx) return footerNativeCtx;
|
|
105
|
+
nativeCtxModel = modelId;
|
|
106
|
+
if (!nativeCtxPromise) {
|
|
107
|
+
nativeCtxPromise = (async () => {
|
|
108
|
+
try {
|
|
109
|
+
const ollamaBase = getOllamaBaseUrl();
|
|
110
|
+
const res = await fetch(`${ollamaBase}/api/show`, {
|
|
111
|
+
method: "POST",
|
|
112
|
+
headers: { "Content-Type": "application/json" },
|
|
113
|
+
body: JSON.stringify({ name: modelId }),
|
|
114
|
+
signal: AbortSignal.timeout(5e3)
|
|
115
|
+
});
|
|
116
|
+
if (!res.ok) return;
|
|
117
|
+
const data = await res.json();
|
|
118
|
+
for (const key of Object.keys(data?.model_info ?? {})) {
|
|
119
|
+
if (key.endsWith(".context_length")) {
|
|
120
|
+
const val = data.model_info[key];
|
|
121
|
+
if (typeof val === "number") {
|
|
122
|
+
footerNativeCtx = val >= 1e3 ? `${(val / 1e3).toFixed(0)}k` : String(val);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
const numCtx = data?.model_info?.["num_ctx"];
|
|
128
|
+
if (typeof numCtx === "number") {
|
|
129
|
+
footerNativeCtx = numCtx >= 1e3 ? `${(numCtx / 1e3).toFixed(0)}k` : String(numCtx);
|
|
130
|
+
}
|
|
131
|
+
} catch {
|
|
132
|
+
} finally {
|
|
133
|
+
nativeCtxPromise = null;
|
|
134
|
+
}
|
|
135
|
+
})();
|
|
136
|
+
}
|
|
137
|
+
return footerNativeCtx;
|
|
138
|
+
}
|
|
99
139
|
function getOllamaLoadedModel() {
|
|
100
140
|
const now = Date.now();
|
|
101
141
|
if (now - ollamaLoadedLastCheck < OLLAMA_LOADED_INTERVAL) return ollamaLoadedCache;
|
|
@@ -128,6 +168,10 @@ function status_temp_default(pi) {
|
|
|
128
168
|
if (payload.reasoning_effort !== void 0) params.push(`think:${payload.reasoning_effort}`);
|
|
129
169
|
return params;
|
|
130
170
|
}
|
|
171
|
+
function fmtTk(n) {
|
|
172
|
+
if (n >= 1e3) return `${(n / 1e3).toFixed(1)}k`;
|
|
173
|
+
return String(n);
|
|
174
|
+
}
|
|
131
175
|
function getPwd() {
|
|
132
176
|
const cwd = process.cwd();
|
|
133
177
|
if (cwd.startsWith(os.homedir())) return "~" + cwd.slice(os.homedir().length);
|
|
@@ -181,6 +225,7 @@ function status_temp_default(pi) {
|
|
|
181
225
|
modelsJson = JSON.parse(raw);
|
|
182
226
|
} catch {
|
|
183
227
|
}
|
|
228
|
+
isLocalProvider = modelsJson ? detectLocalProvider(modelsJson) : false;
|
|
184
229
|
if (currentCtx) {
|
|
185
230
|
footerModel = currentCtx.model?.id || "";
|
|
186
231
|
footerThinking = pi.getThinkingLevel?.() ?? "";
|
|
@@ -192,17 +237,10 @@ function status_temp_default(pi) {
|
|
|
192
237
|
footerCtxPct = "";
|
|
193
238
|
}
|
|
194
239
|
const modelId = currentCtx.model?.id || "";
|
|
195
|
-
if (modelId &&
|
|
196
|
-
|
|
197
|
-
const match = (prov.models || []).find((m) => m.id === modelId);
|
|
198
|
-
if (match?.contextLength) {
|
|
199
|
-
footerCtxPct = `${(match.contextLength / 1e3).toFixed(0)}k ctx`;
|
|
200
|
-
break;
|
|
201
|
-
}
|
|
202
|
-
}
|
|
240
|
+
if (modelId && isLocalProvider) {
|
|
241
|
+
getNativeModelCtx(modelId);
|
|
203
242
|
}
|
|
204
243
|
}
|
|
205
|
-
isLocalProvider = modelsJson ? detectLocalProvider(modelsJson) : false;
|
|
206
244
|
refreshBlockedCount();
|
|
207
245
|
}
|
|
208
246
|
pi.on("session_start", async (_event, ctx) => {
|
|
@@ -216,56 +254,68 @@ function status_temp_default(pi) {
|
|
|
216
254
|
const red = (s) => theme?.fg?.("red", s) ?? s;
|
|
217
255
|
const yellow = (s) => theme?.fg?.("yellow", s) ?? s;
|
|
218
256
|
const sep = dim(" \xB7 ");
|
|
257
|
+
const truncateLine = (line, maxW) => {
|
|
258
|
+
const ellipsis = dim("...");
|
|
259
|
+
const visible = line.replace(/\x1b\[[0-9;]*m/g, "");
|
|
260
|
+
if (visible.length > maxW) {
|
|
261
|
+
let vis = 0, cut = 0;
|
|
262
|
+
for (let i = 0; i < line.length && vis < maxW - 3; i++) {
|
|
263
|
+
if (line[i] === "\x1B") {
|
|
264
|
+
while (i < line.length && line[i] !== "m") i++;
|
|
265
|
+
} else {
|
|
266
|
+
vis++;
|
|
267
|
+
}
|
|
268
|
+
cut = i + 1;
|
|
269
|
+
}
|
|
270
|
+
return line.slice(0, cut) + ellipsis;
|
|
271
|
+
}
|
|
272
|
+
return line;
|
|
273
|
+
};
|
|
219
274
|
return {
|
|
220
275
|
render(width) {
|
|
221
276
|
const lines = [];
|
|
222
|
-
const parts = [];
|
|
223
|
-
parts.push(getPwd());
|
|
224
277
|
let branch = "";
|
|
225
278
|
try {
|
|
226
279
|
branch = footerData?.getGitBranch?.() || "";
|
|
227
280
|
} catch {
|
|
228
281
|
}
|
|
229
282
|
if (!branch) branch = getGitBranch();
|
|
230
|
-
|
|
231
|
-
if (footerModel)
|
|
232
|
-
|
|
233
|
-
if (
|
|
283
|
+
const line1Parts = [];
|
|
284
|
+
if (footerModel) line1Parts.push(`conf:${footerModel}`);
|
|
285
|
+
line1Parts.push(getPwd());
|
|
286
|
+
if (footerThinking && footerThinking !== "off") line1Parts.push(dim(footerThinking));
|
|
234
287
|
if (isLocalProvider) {
|
|
235
|
-
|
|
236
|
-
|
|
288
|
+
line1Parts.push(dim(`CPU ${cpuUsage.toFixed(0)}%`));
|
|
289
|
+
}
|
|
290
|
+
let line1 = truncateLine(line1Parts.join(sep), width);
|
|
291
|
+
lines.push(line1);
|
|
292
|
+
const line2Parts = [];
|
|
293
|
+
if (ollamaLoaded) line2Parts.push(`load:${ollamaLoaded}`);
|
|
294
|
+
if (footerNativeCtx) line2Parts.push(`M:${footerNativeCtx}`);
|
|
295
|
+
if (footerCtxPct) line2Parts.push(`S:${footerCtxPct}`);
|
|
296
|
+
if (isLocalProvider) {
|
|
297
|
+
line2Parts.push(`RAM ${fmtBytes(memUsed)}/${fmtBytes(memTotal)}`);
|
|
237
298
|
if (hasSwap && swapUsed > 0) {
|
|
238
|
-
|
|
299
|
+
line2Parts.push(`Swap ${fmtBytes(swapUsed)}/${fmtBytes(swapTotal)}`);
|
|
239
300
|
}
|
|
240
301
|
}
|
|
241
|
-
if (
|
|
242
|
-
|
|
302
|
+
if (lastUpstream > 0 || lastDownstream > 0) {
|
|
303
|
+
line2Parts.push(dim(`\u2191${fmtTk(lastUpstream)} \u2193${fmtTk(lastDownstream)}`));
|
|
304
|
+
}
|
|
305
|
+
if (lastResponseTime !== null) line2Parts.push(`Resp ${fmtDur(lastResponseTime)}`);
|
|
243
306
|
if (lastPayload) {
|
|
244
307
|
const params = extractParams(lastPayload);
|
|
245
|
-
if (params.length > 0)
|
|
308
|
+
if (params.length > 0) line2Parts.push(...params.map((p) => dim(p)));
|
|
246
309
|
}
|
|
247
310
|
const now = Date.now();
|
|
248
311
|
if (securityFlashTool && now < securityFlashUntil) {
|
|
249
|
-
|
|
312
|
+
line2Parts.push(red(`BLOCKED:${securityFlashTool}`));
|
|
250
313
|
}
|
|
251
314
|
if (blockedCount > 0) {
|
|
252
|
-
|
|
253
|
-
}
|
|
254
|
-
let line = parts.join(sep);
|
|
255
|
-
const visible = line.replace(/\x1b\[[0-9;]*m/g, "");
|
|
256
|
-
if (visible.length > width) {
|
|
257
|
-
let vis = 0, cut = 0;
|
|
258
|
-
for (let i = 0; i < line.length && vis < width - 3; i++) {
|
|
259
|
-
if (line[i] === "\x1B") {
|
|
260
|
-
while (i < line.length && line[i] !== "m") i++;
|
|
261
|
-
} else {
|
|
262
|
-
vis++;
|
|
263
|
-
}
|
|
264
|
-
cut = i + 1;
|
|
265
|
-
}
|
|
266
|
-
line = line.slice(0, cut) + dim("...");
|
|
315
|
+
line2Parts.push(red(`SEC:${blockedCount}`));
|
|
267
316
|
}
|
|
268
|
-
|
|
317
|
+
let line2 = truncateLine(line2Parts.join(sep), width);
|
|
318
|
+
if (line2) lines.push(line2);
|
|
269
319
|
if (activeTool && activeToolStart > 0) {
|
|
270
320
|
const elapsed = performance.now() - activeToolStart;
|
|
271
321
|
lines.push(`${yellow("\u23F3")} ${activeTool}: ${fmtDur(elapsed)}`);
|
|
@@ -298,12 +348,29 @@ function status_temp_default(pi) {
|
|
|
298
348
|
activeTool = "";
|
|
299
349
|
activeToolStart = 0;
|
|
300
350
|
blockedCount = 0;
|
|
351
|
+
lastUpstream = 0;
|
|
352
|
+
lastDownstream = 0;
|
|
301
353
|
});
|
|
302
354
|
pi.on("before_provider_request", (event) => {
|
|
303
355
|
lastPayload = event.payload;
|
|
304
356
|
});
|
|
357
|
+
function captureUsage(event) {
|
|
358
|
+
if (event?.message?.role !== "assistant") return;
|
|
359
|
+
const usage = event?.message?.usage ?? // normalised Pi usage
|
|
360
|
+
event?.usage ?? // alternative path
|
|
361
|
+
null;
|
|
362
|
+
if (!usage) return;
|
|
363
|
+
const inp = usage.input ?? usage.promptTokens ?? usage.prompt_tokens;
|
|
364
|
+
const out = usage.output ?? usage.completionTokens ?? usage.completion_tokens;
|
|
365
|
+
if (inp != null) lastUpstream = inp;
|
|
366
|
+
if (out != null) lastDownstream = out;
|
|
367
|
+
}
|
|
368
|
+
pi.on("message_end", captureUsage);
|
|
369
|
+
pi.on("turn_end", captureUsage);
|
|
305
370
|
pi.on("agent_start", async () => {
|
|
306
371
|
agentStartTime = performance.now();
|
|
372
|
+
lastUpstream = 0;
|
|
373
|
+
lastDownstream = 0;
|
|
307
374
|
});
|
|
308
375
|
pi.on("agent_end", async () => {
|
|
309
376
|
if (agentStartTime !== null) {
|