pi-metrics 0.4.2 → 0.5.0

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-metrics",
3
- "version": "0.4.2",
3
+ "version": "0.5.0",
4
4
  "description": "Pi metrics extension: elapsed-time HUD and resilient TPS telemetry",
5
5
  "type": "module",
6
6
  "main": "./index.ts",
@@ -62,7 +62,7 @@
62
62
  "@earendil-works/pi-tui": ">=0.80.0"
63
63
  },
64
64
  "dependencies": {
65
- "pi-extensions-i18n": "^0.4.1"
65
+ "pi-extensions-i18n": "^0.5.0"
66
66
  },
67
67
  "devDependencies": {
68
68
  "@earendil-works/pi-ai": "0.85.1",
package/src/index.ts CHANGED
@@ -3,7 +3,8 @@ import type {
3
3
  ExtensionCommandContext,
4
4
  ExtensionContext,
5
5
  } from "@earendil-works/pi-coding-agent";
6
- import { createTranslator, loadCatalog } from "pi-extensions-i18n";
6
+ import { createTranslator, loadCatalog, notifyWithSource } from "pi-extensions-i18n";
7
+ import { NOTICE_SOURCE } from "./notice.ts";
7
8
  import turnElapsed from "./turn-elapsed.ts";
8
9
  import tps from "./tps.ts";
9
10
  import { configPath, loadConfig, parseConfig, saveConfig, type MetricsConfig } from "./config.ts";
@@ -29,32 +30,42 @@ function registerConfigCommand(pi: ExtensionAPI): void {
29
30
  handler: async (args: string, ctx: ExtensionCommandContext): Promise<void> => {
30
31
  const value = args.trim();
31
32
  if (value && value !== CONFIG_RESET_COMMAND && value !== ENABLE_COMMAND && value !== DISABLE_COMMAND) {
32
- ctx.ui.notify(i18n.t("configCommandUsage"), NOTICE_WARNING);
33
+ notifyWithSource({ ctx, source: NOTICE_SOURCE, level: NOTICE_WARNING, message: i18n.t("configCommandUsage") });
33
34
  return;
34
35
  }
35
36
  if (value === CONFIG_RESET_COMMAND || value === ENABLE_COMMAND || value === DISABLE_COMMAND) {
36
37
  try {
37
38
  const config = parseConfig(value === CONFIG_RESET_COMMAND ? {} : { enabled: value === ENABLE_COMMAND });
38
39
  const path = saveConfig(config);
39
- ctx.ui.notify(i18n.t("configCommandSaved", { path }), NOTICE_INFO);
40
+ notifyWithSource({ ctx, source: NOTICE_SOURCE, level: NOTICE_INFO, message: i18n.t("configCommandSaved", { path }) });
40
41
  } catch (error) {
41
- ctx.ui.notify(i18n.t("configCommandInvalid", {
42
- error: error instanceof Error ? error.message : String(error),
43
- }), NOTICE_ERROR);
42
+ notifyWithSource({
43
+ ctx,
44
+ source: NOTICE_SOURCE,
45
+ level: NOTICE_ERROR,
46
+ message: i18n.t("configCommandInvalid", {
47
+ error: error instanceof Error ? error.message : String(error),
48
+ }),
49
+ });
44
50
  }
45
51
  return;
46
52
  }
47
53
  if (!ctx.hasUI) {
48
- ctx.ui.notify(i18n.t("configCommandInteractiveOnly"), NOTICE_WARNING);
54
+ notifyWithSource({ ctx, source: NOTICE_SOURCE, level: NOTICE_WARNING, message: i18n.t("configCommandInteractiveOnly") });
49
55
  return;
50
56
  }
51
57
  let current: MetricsConfig;
52
58
  try {
53
59
  current = loadConfig();
54
60
  } catch (error) {
55
- ctx.ui.notify(i18n.t("configCommandInvalid", {
56
- error: error instanceof Error ? error.message : String(error),
57
- }), NOTICE_ERROR);
61
+ notifyWithSource({
62
+ ctx,
63
+ source: NOTICE_SOURCE,
64
+ level: NOTICE_ERROR,
65
+ message: i18n.t("configCommandInvalid", {
66
+ error: error instanceof Error ? error.message : String(error),
67
+ }),
68
+ });
58
69
  return;
59
70
  }
60
71
  const enabledChoice = i18n.t("configEnabled", { value: i18n.t(current.enabled ? "configOn" : "configOff") });
@@ -69,11 +80,16 @@ function registerConfigCommand(pi: ExtensionAPI): void {
69
80
  const choice = selected === enabledChoice ? CONFIG_CHOICE.enabled : CONFIG_CHOICE.disabled;
70
81
  try {
71
82
  const path = saveConfig({ enabled: choice === CONFIG_CHOICE.enabled });
72
- ctx.ui.notify(i18n.t("configCommandSaved", { path }), NOTICE_INFO);
83
+ notifyWithSource({ ctx, source: NOTICE_SOURCE, level: NOTICE_INFO, message: i18n.t("configCommandSaved", { path }) });
73
84
  } catch (error) {
74
- ctx.ui.notify(i18n.t("configCommandInvalid", {
75
- error: error instanceof Error ? error.message : String(error),
76
- }), NOTICE_ERROR);
85
+ notifyWithSource({
86
+ ctx,
87
+ source: NOTICE_SOURCE,
88
+ level: NOTICE_ERROR,
89
+ message: i18n.t("configCommandInvalid", {
90
+ error: error instanceof Error ? error.message : String(error),
91
+ }),
92
+ });
77
93
  }
78
94
  },
79
95
  };
@@ -93,10 +109,15 @@ export default function piHud(pi: ExtensionAPI): void {
93
109
  }
94
110
  if (configError !== undefined) {
95
111
  pi.on("session_start", (_event, ctx: ExtensionContext) => {
96
- ctx.ui.notify(i18n.t("configLoadFailed", {
97
- path: configPath(),
98
- error: configError instanceof Error ? configError.message : String(configError),
99
- }), NOTICE_WARNING);
112
+ notifyWithSource({
113
+ ctx,
114
+ source: NOTICE_SOURCE,
115
+ level: NOTICE_WARNING,
116
+ message: i18n.t("configLoadFailed", {
117
+ path: configPath(),
118
+ error: configError instanceof Error ? configError.message : String(configError),
119
+ }),
120
+ });
100
121
  });
101
122
  }
102
123
  if (!config.enabled) return;
package/src/notice.ts ADDED
@@ -0,0 +1,13 @@
1
+ /**
2
+ * 本扩展统一的提示来源定义。
3
+ * 所有 notifyWithSource 调用共用同一份标签与颜色,避免两个入口不一致。
4
+ */
5
+
6
+ import type { NoticeColor, NoticeSource } from "pi-extensions-i18n";
7
+
8
+ /** 本扩展的提示标签;短且唯一,便于在会话里定位来源。 */
9
+ export const NOTICE_TAG = "metrics";
10
+ /** 提示标签颜色;与其它扩展错开,避免看起来像同一条消息。 */
11
+ export const NOTICE_COLOR: NoticeColor = "dim";
12
+ /** 本扩展的提示来源。 */
13
+ export const NOTICE_SOURCE: NoticeSource = { tag: NOTICE_TAG, color: NOTICE_COLOR };
package/src/tps.ts CHANGED
@@ -11,7 +11,9 @@ import type {
11
11
  ExtensionAPI,
12
12
  ExtensionContext,
13
13
  } from "@earendil-works/pi-coding-agent";
14
+ import { notifyWithSource } from "pi-extensions-i18n";
14
15
  import { i18n } from "./i18n.ts";
16
+ import { NOTICE_SOURCE } from "./notice.ts";
15
17
 
16
18
  interface TurnStartEvent {
17
19
  type: "turn_start";
@@ -330,11 +332,11 @@ function restoreTPSNotification(
330
332
  const data = entry.data as Record<string, unknown> | null | undefined;
331
333
  if (!data) continue;
332
334
  if (typeof data.model === "object" && data.model !== null) {
333
- schedule(() => ctx.ui.notify(composeDisplayString(data as unknown as TurnTelemetry), "info"));
335
+ schedule(() => notifyWithSource({ ctx, source: NOTICE_SOURCE, level: "info", message: composeDisplayString(data as unknown as TurnTelemetry) }));
334
336
  return;
335
337
  }
336
338
  if (typeof data.message === "string") {
337
- schedule(() => ctx.ui.notify(data.message as string, "info"));
339
+ schedule(() => notifyWithSource({ ctx, source: NOTICE_SOURCE, level: "info", message: data.message as string }));
338
340
  return;
339
341
  }
340
342
  }
@@ -392,7 +394,7 @@ export default function tpsExtension(pi: ExtensionAPI): void {
392
394
  committed.telemetry = corrected;
393
395
  pi.appendEntry("tps", corrected);
394
396
  pi.events?.emit("tps:telemetry", corrected);
395
- if (committed.ctx.hasUI) committed.ctx.ui.notify(composeDisplayString(corrected), "info");
397
+ if (committed.ctx.hasUI) notifyWithSource({ ctx: committed.ctx, source: NOTICE_SOURCE, level: "info", message: composeDisplayString(corrected) });
396
398
  });
397
399
 
398
400
  pi.on("session_shutdown", () => {
@@ -513,7 +515,7 @@ export default function tpsExtension(pi: ExtensionAPI): void {
513
515
  };
514
516
  pi.appendEntry("tps", telemetry);
515
517
  pi.events?.emit("tps:telemetry", telemetry);
516
- if (ctx.hasUI) ctx.ui.notify(composeDisplayString(telemetry), "info");
518
+ if (ctx.hasUI) notifyWithSource({ ctx, source: NOTICE_SOURCE, level: "info", message: composeDisplayString(telemetry) });
517
519
  });
518
520
 
519
521
  }
@@ -18,8 +18,10 @@
18
18
  */
19
19
 
20
20
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
21
+ import { notifyWithSource } from "pi-extensions-i18n";
21
22
  import { formatDone, formatTick } from "./format-utils.ts";
22
23
  import { i18n } from "./i18n.ts";
24
+ import { NOTICE_SOURCE } from "./notice.ts";
23
25
 
24
26
  const TICK_MS = 1000;
25
27
 
@@ -74,7 +76,7 @@ export default function (pi: ExtensionAPI) {
74
76
  // 恢复 pi 默认 working 文字(下次 streaming 由 pi 内部重置)
75
77
  ctx.ui.setWorkingMessage(undefined);
76
78
  // 在 chat 流末尾插入一条 dim 灰文本
77
- ctx.ui.notify(i18n.t("elapsedDone", { value: formatDone(elapsed) }), "info");
79
+ notifyWithSource({ ctx, source: NOTICE_SOURCE, level: "info", message: i18n.t("elapsedDone", { value: formatDone(elapsed) }) });
78
80
  });
79
81
 
80
82
  pi.on("agent_end", async (_event, ctx) => {
@@ -93,7 +95,7 @@ export default function (pi: ExtensionAPI) {
93
95
 
94
96
  ctx.ui.setWorkingMessage(undefined);
95
97
  if (runElapsed > 0) {
96
- ctx.ui.notify(i18n.t("elapsedTotal", { value: formatDone(runElapsed) }), "info");
98
+ notifyWithSource({ ctx, source: NOTICE_SOURCE, level: "info", message: i18n.t("elapsedTotal", { value: formatDone(runElapsed) }) });
97
99
  }
98
100
  });
99
101
  }