@vtstech/pi-status 1.0.7 → 1.0.9
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 +113 -39
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vtstech/pi-status",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.9",
|
|
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.9"
|
|
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 = "";
|
|
@@ -94,6 +98,44 @@ function status_temp_default(pi) {
|
|
|
94
98
|
}
|
|
95
99
|
return false;
|
|
96
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
|
+
}
|
|
97
139
|
function getOllamaLoadedModel() {
|
|
98
140
|
const now = Date.now();
|
|
99
141
|
if (now - ollamaLoadedLastCheck < OLLAMA_LOADED_INTERVAL) return ollamaLoadedCache;
|
|
@@ -102,7 +144,11 @@ function status_temp_default(pi) {
|
|
|
102
144
|
const ollamaBase = getOllamaBaseUrl();
|
|
103
145
|
const out = execSync(`curl -s "${ollamaBase}/api/ps"`, { encoding: "utf-8", timeout: 5e3 });
|
|
104
146
|
if (out.trim()) {
|
|
105
|
-
|
|
147
|
+
let data;
|
|
148
|
+
try {
|
|
149
|
+
data = JSON.parse(out.trim());
|
|
150
|
+
} catch {
|
|
151
|
+
}
|
|
106
152
|
const models = data?.models || [];
|
|
107
153
|
if (Array.isArray(models) && models.length > 0) {
|
|
108
154
|
ollamaLoadedCache = models[0].name || models[0].model || "unknown";
|
|
@@ -126,6 +172,10 @@ function status_temp_default(pi) {
|
|
|
126
172
|
if (payload.reasoning_effort !== void 0) params.push(`think:${payload.reasoning_effort}`);
|
|
127
173
|
return params;
|
|
128
174
|
}
|
|
175
|
+
function fmtTk(n) {
|
|
176
|
+
if (n >= 1e3) return `${(n / 1e3).toFixed(1)}k`;
|
|
177
|
+
return String(n);
|
|
178
|
+
}
|
|
129
179
|
function getPwd() {
|
|
130
180
|
const cwd = process.cwd();
|
|
131
181
|
if (cwd.startsWith(os.homedir())) return "~" + cwd.slice(os.homedir().length);
|
|
@@ -179,6 +229,7 @@ function status_temp_default(pi) {
|
|
|
179
229
|
modelsJson = JSON.parse(raw);
|
|
180
230
|
} catch {
|
|
181
231
|
}
|
|
232
|
+
isLocalProvider = modelsJson ? detectLocalProvider(modelsJson) : false;
|
|
182
233
|
if (currentCtx) {
|
|
183
234
|
footerModel = currentCtx.model?.id || "";
|
|
184
235
|
footerThinking = pi.getThinkingLevel?.() ?? "";
|
|
@@ -190,17 +241,10 @@ function status_temp_default(pi) {
|
|
|
190
241
|
footerCtxPct = "";
|
|
191
242
|
}
|
|
192
243
|
const modelId = currentCtx.model?.id || "";
|
|
193
|
-
if (modelId &&
|
|
194
|
-
|
|
195
|
-
const match = (prov.models || []).find((m) => m.id === modelId);
|
|
196
|
-
if (match?.contextLength) {
|
|
197
|
-
footerCtxPct = `${(match.contextLength / 1e3).toFixed(0)}k ctx`;
|
|
198
|
-
break;
|
|
199
|
-
}
|
|
200
|
-
}
|
|
244
|
+
if (modelId && isLocalProvider) {
|
|
245
|
+
getNativeModelCtx(modelId);
|
|
201
246
|
}
|
|
202
247
|
}
|
|
203
|
-
isLocalProvider = modelsJson ? detectLocalProvider(modelsJson) : false;
|
|
204
248
|
refreshBlockedCount();
|
|
205
249
|
}
|
|
206
250
|
pi.on("session_start", async (_event, ctx) => {
|
|
@@ -214,56 +258,68 @@ function status_temp_default(pi) {
|
|
|
214
258
|
const red = (s) => theme?.fg?.("red", s) ?? s;
|
|
215
259
|
const yellow = (s) => theme?.fg?.("yellow", s) ?? s;
|
|
216
260
|
const sep = dim(" \xB7 ");
|
|
261
|
+
const truncateLine = (line, maxW) => {
|
|
262
|
+
const ellipsis = dim("...");
|
|
263
|
+
const visible = line.replace(/\x1b\[[0-9;]*m/g, "");
|
|
264
|
+
if (visible.length > maxW) {
|
|
265
|
+
let vis = 0, cut = 0;
|
|
266
|
+
for (let i = 0; i < line.length && vis < maxW - 3; i++) {
|
|
267
|
+
if (line[i] === "\x1B") {
|
|
268
|
+
while (i < line.length && line[i] !== "m") i++;
|
|
269
|
+
} else {
|
|
270
|
+
vis++;
|
|
271
|
+
}
|
|
272
|
+
cut = i + 1;
|
|
273
|
+
}
|
|
274
|
+
return line.slice(0, cut) + ellipsis;
|
|
275
|
+
}
|
|
276
|
+
return line;
|
|
277
|
+
};
|
|
217
278
|
return {
|
|
218
279
|
render(width) {
|
|
219
280
|
const lines = [];
|
|
220
|
-
const parts = [];
|
|
221
|
-
parts.push(getPwd());
|
|
222
281
|
let branch = "";
|
|
223
282
|
try {
|
|
224
283
|
branch = footerData?.getGitBranch?.() || "";
|
|
225
284
|
} catch {
|
|
226
285
|
}
|
|
227
286
|
if (!branch) branch = getGitBranch();
|
|
228
|
-
|
|
229
|
-
if (footerModel)
|
|
230
|
-
|
|
231
|
-
if (
|
|
287
|
+
const line1Parts = [];
|
|
288
|
+
if (footerModel) line1Parts.push(`conf:${footerModel}`);
|
|
289
|
+
line1Parts.push(getPwd());
|
|
290
|
+
if (footerThinking && footerThinking !== "off") line1Parts.push(dim(footerThinking));
|
|
291
|
+
if (isLocalProvider) {
|
|
292
|
+
line1Parts.push(dim(`CPU ${cpuUsage.toFixed(0)}%`));
|
|
293
|
+
}
|
|
294
|
+
let line1 = truncateLine(line1Parts.join(sep), width);
|
|
295
|
+
lines.push(line1);
|
|
296
|
+
const line2Parts = [];
|
|
297
|
+
if (ollamaLoaded) line2Parts.push(`load:${ollamaLoaded}`);
|
|
298
|
+
if (footerNativeCtx) line2Parts.push(`M:${footerNativeCtx}`);
|
|
299
|
+
if (footerCtxPct) line2Parts.push(`S:${footerCtxPct}`);
|
|
232
300
|
if (isLocalProvider) {
|
|
233
|
-
|
|
234
|
-
parts.push(`RAM ${fmtBytes(memUsed)}/${fmtBytes(memTotal)}`);
|
|
301
|
+
line2Parts.push(`RAM ${fmtBytes(memUsed)}/${fmtBytes(memTotal)}`);
|
|
235
302
|
if (hasSwap && swapUsed > 0) {
|
|
236
|
-
|
|
303
|
+
line2Parts.push(`Swap ${fmtBytes(swapUsed)}/${fmtBytes(swapTotal)}`);
|
|
237
304
|
}
|
|
238
305
|
}
|
|
239
|
-
if (
|
|
240
|
-
|
|
306
|
+
if (lastUpstream > 0 || lastDownstream > 0) {
|
|
307
|
+
line2Parts.push(dim(`\u2191${fmtTk(lastUpstream)} \u2193${fmtTk(lastDownstream)}`));
|
|
308
|
+
}
|
|
309
|
+
if (lastResponseTime !== null) line2Parts.push(`Resp ${fmtDur(lastResponseTime)}`);
|
|
241
310
|
if (lastPayload) {
|
|
242
311
|
const params = extractParams(lastPayload);
|
|
243
|
-
if (params.length > 0)
|
|
312
|
+
if (params.length > 0) line2Parts.push(...params.map((p) => dim(p)));
|
|
244
313
|
}
|
|
245
314
|
const now = Date.now();
|
|
246
315
|
if (securityFlashTool && now < securityFlashUntil) {
|
|
247
|
-
|
|
316
|
+
line2Parts.push(red(`BLOCKED:${securityFlashTool}`));
|
|
248
317
|
}
|
|
249
318
|
if (blockedCount > 0) {
|
|
250
|
-
|
|
251
|
-
}
|
|
252
|
-
let line = parts.join(sep);
|
|
253
|
-
const visible = line.replace(/\x1b\[[0-9;]*m/g, "");
|
|
254
|
-
if (visible.length > width) {
|
|
255
|
-
let vis = 0, cut = 0;
|
|
256
|
-
for (let i = 0; i < line.length && vis < width - 3; i++) {
|
|
257
|
-
if (line[i] === "\x1B") {
|
|
258
|
-
while (i < line.length && line[i] !== "m") i++;
|
|
259
|
-
} else {
|
|
260
|
-
vis++;
|
|
261
|
-
}
|
|
262
|
-
cut = i + 1;
|
|
263
|
-
}
|
|
264
|
-
line = line.slice(0, cut) + dim("...");
|
|
319
|
+
line2Parts.push(red(`SEC:${blockedCount}`));
|
|
265
320
|
}
|
|
266
|
-
|
|
321
|
+
let line2 = truncateLine(line2Parts.join(sep), width);
|
|
322
|
+
if (line2) lines.push(line2);
|
|
267
323
|
if (activeTool && activeToolStart > 0) {
|
|
268
324
|
const elapsed = performance.now() - activeToolStart;
|
|
269
325
|
lines.push(`${yellow("\u23F3")} ${activeTool}: ${fmtDur(elapsed)}`);
|
|
@@ -296,12 +352,30 @@ function status_temp_default(pi) {
|
|
|
296
352
|
activeTool = "";
|
|
297
353
|
activeToolStart = 0;
|
|
298
354
|
blockedCount = 0;
|
|
355
|
+
lastUpstream = 0;
|
|
356
|
+
lastDownstream = 0;
|
|
299
357
|
});
|
|
300
358
|
pi.on("before_provider_request", (event) => {
|
|
301
359
|
lastPayload = event.payload;
|
|
302
360
|
});
|
|
361
|
+
function captureUsage(event) {
|
|
362
|
+
if (event?.message?.role !== "assistant") return;
|
|
363
|
+
const usage = event?.message?.usage ?? // normalised Pi usage
|
|
364
|
+
event?.usage ?? // alternative path
|
|
365
|
+
null;
|
|
366
|
+
if (!usage) return;
|
|
367
|
+
const inp = usage.input ?? usage.promptTokens ?? usage.prompt_tokens;
|
|
368
|
+
const out = usage.output ?? usage.completionTokens ?? usage.completion_tokens;
|
|
369
|
+
if (inp != null) lastUpstream = inp;
|
|
370
|
+
if (out != null) lastDownstream = out;
|
|
371
|
+
if (tuiRef) tuiRef.requestRender();
|
|
372
|
+
}
|
|
373
|
+
pi.on("message_end", captureUsage);
|
|
374
|
+
pi.on("turn_end", captureUsage);
|
|
303
375
|
pi.on("agent_start", async () => {
|
|
304
376
|
agentStartTime = performance.now();
|
|
377
|
+
lastUpstream = 0;
|
|
378
|
+
lastDownstream = 0;
|
|
305
379
|
});
|
|
306
380
|
pi.on("agent_end", async () => {
|
|
307
381
|
if (agentStartTime !== null) {
|