@vtstech/pi-status 1.0.7 → 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.
Files changed (2) hide show
  1. package/package.json +2 -2
  2. package/status.js +107 -38
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vtstech/pi-status",
3
- "version": "1.0.7",
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.7"
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 = "";
@@ -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;
@@ -126,6 +168,10 @@ function status_temp_default(pi) {
126
168
  if (payload.reasoning_effort !== void 0) params.push(`think:${payload.reasoning_effort}`);
127
169
  return params;
128
170
  }
171
+ function fmtTk(n) {
172
+ if (n >= 1e3) return `${(n / 1e3).toFixed(1)}k`;
173
+ return String(n);
174
+ }
129
175
  function getPwd() {
130
176
  const cwd = process.cwd();
131
177
  if (cwd.startsWith(os.homedir())) return "~" + cwd.slice(os.homedir().length);
@@ -179,6 +225,7 @@ function status_temp_default(pi) {
179
225
  modelsJson = JSON.parse(raw);
180
226
  } catch {
181
227
  }
228
+ isLocalProvider = modelsJson ? detectLocalProvider(modelsJson) : false;
182
229
  if (currentCtx) {
183
230
  footerModel = currentCtx.model?.id || "";
184
231
  footerThinking = pi.getThinkingLevel?.() ?? "";
@@ -190,17 +237,10 @@ function status_temp_default(pi) {
190
237
  footerCtxPct = "";
191
238
  }
192
239
  const modelId = currentCtx.model?.id || "";
193
- if (modelId && !footerCtxPct && modelsJson) {
194
- for (const prov of Object.values(modelsJson.providers || {})) {
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
- }
240
+ if (modelId && isLocalProvider) {
241
+ getNativeModelCtx(modelId);
201
242
  }
202
243
  }
203
- isLocalProvider = modelsJson ? detectLocalProvider(modelsJson) : false;
204
244
  refreshBlockedCount();
205
245
  }
206
246
  pi.on("session_start", async (_event, ctx) => {
@@ -214,56 +254,68 @@ function status_temp_default(pi) {
214
254
  const red = (s) => theme?.fg?.("red", s) ?? s;
215
255
  const yellow = (s) => theme?.fg?.("yellow", s) ?? s;
216
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
+ };
217
274
  return {
218
275
  render(width) {
219
276
  const lines = [];
220
- const parts = [];
221
- parts.push(getPwd());
222
277
  let branch = "";
223
278
  try {
224
279
  branch = footerData?.getGitBranch?.() || "";
225
280
  } catch {
226
281
  }
227
282
  if (!branch) branch = getGitBranch();
228
- if (branch) parts.push(dim(branch));
229
- if (footerModel) parts.push(dim(footerModel));
230
- if (footerThinking && footerThinking !== "off") parts.push(dim(footerThinking));
231
- if (footerCtxPct) parts.push(footerCtxPct);
283
+ const line1Parts = [];
284
+ if (footerModel) line1Parts.push(`conf:${footerModel}`);
285
+ line1Parts.push(getPwd());
286
+ if (footerThinking && footerThinking !== "off") line1Parts.push(dim(footerThinking));
287
+ if (isLocalProvider) {
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}`);
232
296
  if (isLocalProvider) {
233
- parts.push(dim(`CPU ${cpuUsage.toFixed(0)}%`));
234
- parts.push(`RAM ${fmtBytes(memUsed)}/${fmtBytes(memTotal)}`);
297
+ line2Parts.push(`RAM ${fmtBytes(memUsed)}/${fmtBytes(memTotal)}`);
235
298
  if (hasSwap && swapUsed > 0) {
236
- parts.push(`Swap ${fmtBytes(swapUsed)}/${fmtBytes(swapTotal)}`);
299
+ line2Parts.push(`Swap ${fmtBytes(swapUsed)}/${fmtBytes(swapTotal)}`);
237
300
  }
238
301
  }
239
- if (ollamaLoaded) parts.push(`${ollamaLoaded}`);
240
- if (lastResponseTime !== null) parts.push(`Resp ${fmtDur(lastResponseTime)}`);
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)}`);
241
306
  if (lastPayload) {
242
307
  const params = extractParams(lastPayload);
243
- if (params.length > 0) parts.push(...params.map((p) => dim(p)));
308
+ if (params.length > 0) line2Parts.push(...params.map((p) => dim(p)));
244
309
  }
245
310
  const now = Date.now();
246
311
  if (securityFlashTool && now < securityFlashUntil) {
247
- parts.push(red(`BLOCKED:${securityFlashTool}`));
312
+ line2Parts.push(red(`BLOCKED:${securityFlashTool}`));
248
313
  }
249
314
  if (blockedCount > 0) {
250
- parts.push(red(`SEC:${blockedCount}`));
315
+ line2Parts.push(red(`SEC:${blockedCount}`));
251
316
  }
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("...");
265
- }
266
- lines.push(line);
317
+ let line2 = truncateLine(line2Parts.join(sep), width);
318
+ if (line2) lines.push(line2);
267
319
  if (activeTool && activeToolStart > 0) {
268
320
  const elapsed = performance.now() - activeToolStart;
269
321
  lines.push(`${yellow("\u23F3")} ${activeTool}: ${fmtDur(elapsed)}`);
@@ -296,12 +348,29 @@ function status_temp_default(pi) {
296
348
  activeTool = "";
297
349
  activeToolStart = 0;
298
350
  blockedCount = 0;
351
+ lastUpstream = 0;
352
+ lastDownstream = 0;
299
353
  });
300
354
  pi.on("before_provider_request", (event) => {
301
355
  lastPayload = event.payload;
302
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);
303
370
  pi.on("agent_start", async () => {
304
371
  agentStartTime = performance.now();
372
+ lastUpstream = 0;
373
+ lastDownstream = 0;
305
374
  });
306
375
  pi.on("agent_end", async () => {
307
376
  if (agentStartTime !== null) {