token-stats-timer 1.0.12 → 1.0.13
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/README.md +2 -5
- package/notify.ts +1 -27
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -40,8 +40,6 @@ Footer 下行:cwd + git 分支 + 其他扩展状态。
|
|
|
40
40
|
|
|
41
41
|
> 为什么不用纯 osascript:macOS Sequoia 起通知按“调用进程”归属,pi 是 node 进程、无法在系统设置里授权,通知会被静默丢弃(脚本 exit 0 但没弹窗)。OSC 序列把归属改到终端 App,授权后即可正常弹。
|
|
42
42
|
|
|
43
|
-
每次投递结果追加到 `~/.pi/agent/extensions/token-stats-logs/notify.log`,没弹通知时先看它判断是触发问题还是投递问题。
|
|
44
|
-
|
|
45
43
|
### 配置
|
|
46
44
|
|
|
47
45
|
配置文件:`~/.pi/agent/extensions/token-stats/notify-config.json`(不存在时用默认值)
|
|
@@ -63,7 +61,7 @@ Footer 下行:cwd + git 分支 + 其他扩展状态。
|
|
|
63
61
|
- `sound` —— 通知声音(macOS 内置:Glass/Ping/Sosumi/Hero/Funk 等,`""` 静音;注意仅 osascript/terminal-notifier 通道有声,OSC 777 无声音参数)
|
|
64
62
|
- `onSuccess/onFailure/onAbort/onSessionEnd` —— 各类通知开关
|
|
65
63
|
|
|
66
|
-
`/notify` —— 无参查看当前状态;`on`/`off` 开关;`test`
|
|
64
|
+
`/notify` —— 无参查看当前状态;`on`/`off` 开关;`test` 立即发一条测试通知验证通道是否可达。
|
|
67
65
|
|
|
68
66
|
## 任务计时(step-timer)
|
|
69
67
|
|
|
@@ -83,11 +81,10 @@ Footer 下行:cwd + git 分支 + 其他扩展状态。
|
|
|
83
81
|
- `/stats config` —— 显示样式 / 显示内容 / 配额刷新时间
|
|
84
82
|
- `/notify [on|off|test]` —— 通知开关 / 测试(无参查看状态)
|
|
85
83
|
|
|
86
|
-
##
|
|
84
|
+
## 与原包的兼容性
|
|
87
85
|
|
|
88
86
|
- 显示/套餐配置沿用 `~/.pi/agent/extensions/token-stats/`(原 token-stats 的配置直接生效)
|
|
89
87
|
- 统计日志沿用 `~/.pi/agent/extensions/token-stats-logs/`(历史数据 `/stats` 可直接查询)
|
|
90
|
-
- run 计时状态沿用 session 内 `run-timer-state` 自定义条目(`/reload` 后自动恢复,兼容旧 `turn-timer-state`)
|
|
91
88
|
|
|
92
89
|
## 安装(替换原包)
|
|
93
90
|
|
package/notify.ts
CHANGED
|
@@ -41,8 +41,6 @@ import { join } from "node:path";
|
|
|
41
41
|
|
|
42
42
|
const CONFIG_DIR = join(homedir(), ".pi/agent/extensions/token-stats");
|
|
43
43
|
const CONFIG_FILE = join(CONFIG_DIR, "notify-config.json");
|
|
44
|
-
const LOG_DIR = join(homedir(), ".pi/agent/extensions/token-stats-logs");
|
|
45
|
-
const NOTIFY_LOG = join(LOG_DIR, "notify.log");
|
|
46
44
|
|
|
47
45
|
/** 支持 OSC 777 终端协议的应用(按 TERM_PROGRAM 匹配) */
|
|
48
46
|
const OSC777_TERMINALS = new Set([
|
|
@@ -91,19 +89,6 @@ function saveConfig(cfg: NotifyConfig): void {
|
|
|
91
89
|
/** 连续两次通知的最短间隔(毫秒),防止并行 agent 结束时刷屏 */
|
|
92
90
|
const DEBOUNCE_MS = 3000;
|
|
93
91
|
|
|
94
|
-
function logLine(channel: string, title: string, ok: boolean, detail = ""): void {
|
|
95
|
-
try {
|
|
96
|
-
mkdirSync(LOG_DIR, { recursive: true });
|
|
97
|
-
appendFileSync(
|
|
98
|
-
NOTIFY_LOG,
|
|
99
|
-
`${new Date().toISOString()} [${channel}] ${ok ? "OK" : "FAIL"} ${JSON.stringify(title)}${detail ? ` ${detail}` : ""}\n`,
|
|
100
|
-
"utf-8",
|
|
101
|
-
);
|
|
102
|
-
} catch {
|
|
103
|
-
// 日志失败不影响通知本身
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
92
|
function formatDuration(ms: number): string {
|
|
108
93
|
const totalSeconds = Math.max(0, Math.floor(ms / 1000));
|
|
109
94
|
const hours = Math.floor(totalSeconds / 3600);
|
|
@@ -173,7 +158,7 @@ function notifyTerminalNotifier(title: string, message: string, sound: string):
|
|
|
173
158
|
}
|
|
174
159
|
}
|
|
175
160
|
|
|
176
|
-
/** 通道3:osascript 保底(不支持 OSC 777
|
|
161
|
+
/** 通道3:osascript 保底(不支持 OSC 777 的终端) */
|
|
177
162
|
function notifyOSAScript(title: string, message: string, sound: string): void {
|
|
178
163
|
const soundPart = sound ? ` sound name ${JSON.stringify(sound)}` : "";
|
|
179
164
|
const script = `display notification ${JSON.stringify(message)} with title ${JSON.stringify(title)}${soundPart}`;
|
|
@@ -181,14 +166,6 @@ function notifyOSAScript(title: string, message: string, sound: string): void {
|
|
|
181
166
|
detached: true,
|
|
182
167
|
stdio: ["ignore", "ignore", "pipe"],
|
|
183
168
|
});
|
|
184
|
-
let err = "";
|
|
185
|
-
child.stderr?.on("data", (d) => {
|
|
186
|
-
err += d.toString();
|
|
187
|
-
});
|
|
188
|
-
child.on("error", (e) => logLine("osascript", title, false, e.message));
|
|
189
|
-
child.on("exit", (code) => {
|
|
190
|
-
if (code !== 0) logLine("osascript", title, false, err.trim() || `exit ${code}`);
|
|
191
|
-
});
|
|
192
169
|
child.unref();
|
|
193
170
|
}
|
|
194
171
|
|
|
@@ -213,19 +190,16 @@ export function createNotifier(pi: ExtensionAPI): void {
|
|
|
213
190
|
if (isITerm2()) {
|
|
214
191
|
// iTerm2:官方文档通知序列 OSC 9(实测有效)
|
|
215
192
|
notifyOSC9(title, message);
|
|
216
|
-
logLine("osc9", title, true);
|
|
217
193
|
return;
|
|
218
194
|
}
|
|
219
195
|
|
|
220
196
|
if (supportsOSC777()) {
|
|
221
197
|
// Ghostty / WezTerm / Hyper / rxvt:OSC 777
|
|
222
198
|
notifyOSC777(title, message);
|
|
223
|
-
logLine("osc777", title, true);
|
|
224
199
|
return;
|
|
225
200
|
}
|
|
226
201
|
|
|
227
202
|
if (notifyTerminalNotifier(title, message, sound)) {
|
|
228
|
-
logLine("terminal-notifier", title, true);
|
|
229
203
|
return;
|
|
230
204
|
}
|
|
231
205
|
|