@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.
Files changed (2) hide show
  1. package/package.json +2 -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.6",
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.6"
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
- for (const provider of Object.values(modelsJson.providers || {})) {
82
- const url = provider.baseUrl || "";
83
- const hasLocalUrl = url.includes("localhost") || url.includes("127.0.0.1") || url.includes("0.0.0.0");
84
- const modelId = footerModel || "";
85
- if (modelId && (provider.models || []).some((m) => m.id === modelId)) {
86
- return hasLocalUrl;
87
- }
88
- }
89
- for (const [name, provider] of Object.entries(modelsJson.providers || {})) {
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 && !footerCtxPct && modelsJson) {
196
- for (const prov of Object.values(modelsJson.providers || {})) {
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
- if (branch) parts.push(dim(branch));
231
- if (footerModel) parts.push(dim(footerModel));
232
- if (footerThinking && footerThinking !== "off") parts.push(dim(footerThinking));
233
- 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));
234
287
  if (isLocalProvider) {
235
- parts.push(dim(`CPU ${cpuUsage.toFixed(0)}%`));
236
- parts.push(`RAM ${fmtBytes(memUsed)}/${fmtBytes(memTotal)}`);
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
- parts.push(`Swap ${fmtBytes(swapUsed)}/${fmtBytes(swapTotal)}`);
299
+ line2Parts.push(`Swap ${fmtBytes(swapUsed)}/${fmtBytes(swapTotal)}`);
239
300
  }
240
301
  }
241
- if (ollamaLoaded) parts.push(`${ollamaLoaded}`);
242
- 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)}`);
243
306
  if (lastPayload) {
244
307
  const params = extractParams(lastPayload);
245
- if (params.length > 0) parts.push(...params.map((p) => dim(p)));
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
- parts.push(red(`BLOCKED:${securityFlashTool}`));
312
+ line2Parts.push(red(`BLOCKED:${securityFlashTool}`));
250
313
  }
251
314
  if (blockedCount > 0) {
252
- parts.push(red(`SEC:${blockedCount}`));
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
- lines.push(line);
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) {