@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.
Files changed (2) hide show
  1. package/package.json +2 -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.7",
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.7"
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
- const data = JSON.parse(out.trim());
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 && !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
- }
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
- 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);
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
- parts.push(dim(`CPU ${cpuUsage.toFixed(0)}%`));
234
- parts.push(`RAM ${fmtBytes(memUsed)}/${fmtBytes(memTotal)}`);
301
+ line2Parts.push(`RAM ${fmtBytes(memUsed)}/${fmtBytes(memTotal)}`);
235
302
  if (hasSwap && swapUsed > 0) {
236
- parts.push(`Swap ${fmtBytes(swapUsed)}/${fmtBytes(swapTotal)}`);
303
+ line2Parts.push(`Swap ${fmtBytes(swapUsed)}/${fmtBytes(swapTotal)}`);
237
304
  }
238
305
  }
239
- if (ollamaLoaded) parts.push(`${ollamaLoaded}`);
240
- if (lastResponseTime !== null) parts.push(`Resp ${fmtDur(lastResponseTime)}`);
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) parts.push(...params.map((p) => dim(p)));
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
- parts.push(red(`BLOCKED:${securityFlashTool}`));
316
+ line2Parts.push(red(`BLOCKED:${securityFlashTool}`));
248
317
  }
249
318
  if (blockedCount > 0) {
250
- parts.push(red(`SEC:${blockedCount}`));
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
- lines.push(line);
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) {