@tsa-group/claude-usage 0.4.3 → 0.4.5
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/dist/health.js +10 -2
- package/dist/install.js +38 -12
- package/package.json +1 -1
package/dist/health.js
CHANGED
|
@@ -22,6 +22,8 @@ export const STATUS_BY_CODE = {
|
|
|
22
22
|
/** 連續失敗幾次 / 幾小時沒成功,就算不健康 */
|
|
23
23
|
export const WARN_FAILURES = 3;
|
|
24
24
|
export const WARN_STALE_HOURS = 6;
|
|
25
|
+
/** 最近這麼多分鐘內成功過,就視為「現在是好的」,蓋過失敗計數器(見 healthWarning) */
|
|
26
|
+
export const RECENT_OK_MIN = 45;
|
|
25
27
|
/** usage_snapshots.jsonl 的最後一行(本機最新一筆快照) */
|
|
26
28
|
export function lastSnapshot() {
|
|
27
29
|
const p = snapPath();
|
|
@@ -77,11 +79,17 @@ export function saveHealth(h) {
|
|
|
77
79
|
* 不會再推一次。
|
|
78
80
|
*/
|
|
79
81
|
export function healthWarning(h) {
|
|
82
|
+
const ok = parseIso(h.last_ok_at);
|
|
83
|
+
const okMinAgo = ok ? (Date.now() - ok.getTime()) / 60_000 : Infinity;
|
|
80
84
|
const cf = h.consecutive_failures ?? 0;
|
|
81
|
-
|
|
85
|
+
// ★ 近期成功過就不看失敗計數器。「現在有沒有在運作」最可靠的答案是「最近有沒有
|
|
86
|
+
// 成功過」,而不是一個可能卡住的計數器 —— 實測(Molly, 0.4.3)出現過快照確實
|
|
87
|
+
// 每 15 分鐘進來、consecutive_failures 卻停在 152 的矛盾狀態:多個 tick 行程
|
|
88
|
+
// 同時 read-modify-write health.json 就會互相蓋掉,計數器因此永遠歸不了零。
|
|
89
|
+
// 那種情況下報「連續 152 次失敗」是錯的,而且會讓人去修一個沒有壞的東西。
|
|
90
|
+
if (cf >= WARN_FAILURES && okMinAgo > RECENT_OK_MIN) {
|
|
82
91
|
return `連續 ${cf} 次採樣失敗 (${h.last_sample_status}) 自 ${h.first_failure_at}`;
|
|
83
92
|
}
|
|
84
|
-
const ok = parseIso(h.last_ok_at);
|
|
85
93
|
if (ok) {
|
|
86
94
|
const hrs = (Date.now() - ok.getTime()) / 3_600_000;
|
|
87
95
|
if (hrs >= WARN_STALE_HOURS) {
|
package/dist/install.js
CHANGED
|
@@ -119,7 +119,9 @@ function winInstall() {
|
|
|
119
119
|
console.error(` 手動執行這行可看到正確訊息: schtasks ${args.map((a) => (a.includes(" ") ? `"${a}"` : a)).join(" ")}`);
|
|
120
120
|
}
|
|
121
121
|
console.log(`uninstall: schtasks /Delete /TN ${WIN_TASK} /F`);
|
|
122
|
-
console.log("!
|
|
122
|
+
console.log("! 憑證與採樣已於 2026-08-28 在 Windows 實機驗證通過(含桌面版加密憑證庫)。\n" +
|
|
123
|
+
" 尚未完整驗證的是背景任務本身(schtasks + VBS 隱藏視窗)——\n" +
|
|
124
|
+
" 裝完請跑 claude-usage status 確認有心跳。");
|
|
123
125
|
return r.status ?? 1;
|
|
124
126
|
}
|
|
125
127
|
function winUninstall() {
|
|
@@ -128,19 +130,43 @@ function winUninstall() {
|
|
|
128
130
|
rmSync(VBS_PATH());
|
|
129
131
|
return 0;
|
|
130
132
|
}
|
|
133
|
+
/** schtasks 的 LastTaskResult。0 以外的值多半代表背景任務其實沒跑成功。 */
|
|
134
|
+
const TASK_RESULT = {
|
|
135
|
+
0: "上次執行成功",
|
|
136
|
+
267011: "尚未執行過",
|
|
137
|
+
267009: "正在執行中",
|
|
138
|
+
267014: "上次被中止",
|
|
139
|
+
};
|
|
131
140
|
function winStatus() {
|
|
132
|
-
//
|
|
133
|
-
//
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
141
|
+
// ★ 不要印 schtasks 的原始輸出:它走**主控台代碼頁**(繁中是 CP950),而 Node 只會
|
|
142
|
+
// 用 utf8/latin1 解,中文必定變亂碼(實測「下午 03:45 就緒」印成 `¤U¤È 03:45 ´N°ü`)。
|
|
143
|
+
// 先前的版本用正則挑日期迴避,但仍會連帶吃到後面的本地化文字。
|
|
144
|
+
// 改走 PowerShell 的 ScheduledTasks cmdlet,**自己指定 ASCII 安全的輸出格式** ——
|
|
145
|
+
// 順便拿到 LastTaskResult,那正是「背景任務上次到底有沒有跑成功」的直接答案,
|
|
146
|
+
// 而那是 schtasks 表格裡最該看、卻最容易被亂碼蓋掉的一欄。
|
|
147
|
+
const cmd = `$ErrorActionPreference='SilentlyContinue';` +
|
|
148
|
+
`$t=Get-ScheduledTask -TaskName '${WIN_TASK}';` +
|
|
149
|
+
`if(-not $t){'MISSING'}else{` +
|
|
150
|
+
`$i=Get-ScheduledTaskInfo -TaskName '${WIN_TASK}';` +
|
|
151
|
+
`$last=if($i.LastRunTime){$i.LastRunTime.ToString('yyyy-MM-dd HH:mm')}else{'-'};` +
|
|
152
|
+
`$next=if($i.NextRunTime){$i.NextRunTime.ToString('yyyy-MM-dd HH:mm')}else{'-'};` +
|
|
153
|
+
`"$($t.State)|$($i.LastTaskResult)|$last|$next"}`;
|
|
154
|
+
const r = spawnSync("powershell.exe", ["-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-Command", cmd], { encoding: "utf8", timeout: 15_000, windowsHide: true });
|
|
155
|
+
const line = (r.stdout ?? "").trim();
|
|
156
|
+
if (r.status !== 0 || !line || line === "MISSING") {
|
|
157
|
+
console.log(`bg-task: NOT installed —— 跑 claude-usage install 重新註冊`);
|
|
158
|
+
return 1;
|
|
159
|
+
}
|
|
160
|
+
const [state, resultRaw, last, next] = line.split("|");
|
|
161
|
+
const result = Number(resultRaw);
|
|
162
|
+
const meaning = TASK_RESULT[result] ?? `結束碼 ${resultRaw}`;
|
|
163
|
+
console.log(`bg-task: 已註冊 "${WIN_TASK}" 狀態=${state} 上次=${last} 下次=${next}`);
|
|
164
|
+
console.log(` ${meaning}`);
|
|
165
|
+
// 只有「成功」與「還沒跑過」算正常;其餘代表背景任務其實沒在做事。
|
|
166
|
+
if (result !== 0 && result !== 267011 && result !== 267009) {
|
|
167
|
+
console.log(` ! 背景任務上次沒有正常結束 —— 細節: schtasks /Query /TN ${WIN_TASK} /V /FO LIST`);
|
|
168
|
+
return 1;
|
|
138
169
|
}
|
|
139
|
-
// 只挑出下次執行時間那一段數字/日期,避開會亂碼的本地化欄位標題
|
|
140
|
-
const raw = (r.stdout ?? Buffer.alloc(0)).toString("latin1");
|
|
141
|
-
const when = raw.match(/\d{4}[/-]\d{1,2}[/-]\d{1,2}[^\r\n]*/)?.[0]?.trim();
|
|
142
|
-
console.log(`bg-task: 已註冊 "${WIN_TASK}"${when ? `,下次執行 ${when}` : ""}`);
|
|
143
|
-
console.log(` 完整資訊: schtasks /Query /TN ${WIN_TASK} /V /FO LIST`);
|
|
144
170
|
return 0;
|
|
145
171
|
}
|
|
146
172
|
const sessionStartGroups = (s) => s.hooks?.SessionStart ?? [];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tsa-group/claude-usage",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.5",
|
|
4
4
|
"description": "Per-user Claude usage collector — measures Claude Code token detail and account-level rate-limit utilization locally, reports to your own ingest server.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|