@tsa-group/claude-usage 0.4.0 → 0.4.1

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 CHANGED
@@ -64,6 +64,34 @@ claude-usage install
64
64
  claude-usage status
65
65
  ```
66
66
 
67
+ ### Windows / PowerShell 首次執行
68
+
69
+ 多數 Windows 的 PowerShell 執行原則預設是 `Restricted`,會擋掉 npm 產生的 `.ps1`
70
+ 啟動器 —— **安裝成功、一執行就被擋**:
71
+
72
+ ```
73
+ 因為這個系統上已停用指令碼執行,所以無法載入 ...\npm\claude-usage.ps1
74
+ + FullyQualifiedErrorId : UnauthorizedAccess (PSSecurityException)
75
+ ```
76
+
77
+ 這**不是套件的問題**,`cmd.exe` 與 Node 本身都不受影響。擇一處理:
78
+
79
+ ```powershell
80
+ # 1) 設定一次、永久生效、免系統管理員(推薦)
81
+ Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned
82
+
83
+ # 2) 不改原則,改叫 .cmd 啟動器(功能完全一樣)
84
+ claude-usage.cmd status
85
+ ```
86
+
87
+ `RemoteSigned` = 本機與 npm 的腳本可跑,只擋網路下載的未簽章腳本。
88
+ 公司用 GPO 鎖了機器層級原則時 (1) 會被覆寫 —— 直接用 (2)。
89
+ 現況查詢:`Get-ExecutionPolicy -List`
90
+
91
+ > 安裝時若偵測到會擋的原則,`postinstall` 會主動提醒;事後也可用
92
+ > `claude-usage.cmd doctor` 檢查。**但工具無法在被擋的當下自己說話** ——
93
+ > PowerShell 在 Node 啟動前就拒絕了,所以這段提示只能寫在這裡。
94
+
67
95
  **兩個可能讓你以為「裝壞了」的情況:**
68
96
 
69
97
  - **server 開了人工核准** → `enroll` 會回 `status: pending`,你的資料要等管理者核准後才
@@ -222,8 +250,9 @@ Client 對 server 只用兩個端點,皆為 `application/json`:
222
250
  風險類別。失效時會在 `claude-usage doctor` 顯示卡在哪一步,不會靜默。
223
251
  - **Windows 背景任務未完整實機驗證**:schtasks + VBS 隱藏視窗啟動器。裝完請用
224
252
  `claude-usage status` 確認有心跳。
225
- - **PowerShell 執行原則**:若整台停用指令碼執行,`npm i -g` 會失敗(載不進 `npm.ps1`)。
226
- `npm.cmd i -g …`,或 `Set-ExecutionPolicy -Scope CurrentUser RemoteSigned`(免管理員)。
253
+ - **PowerShell 執行原則**:預設 `Restricted` 會同時擋掉 `npm.ps1`(安裝時)與
254
+ `claude-usage.ps1`(執行時)。安裝用 `npm.cmd i -g …`;其餘見上面
255
+ 「Windows / PowerShell 首次執行」。
227
256
  - **Linux 背景任務未實作**(systemd --user timer)。其餘指令可用。
228
257
  - **`session_id` 跨 compaction / resume 不穩定**:session **數**會高估。token 與成本不受影響
229
258
  (那是 per-event 去重的,與 session 身份無關)。
package/dist/doctor.js CHANGED
@@ -9,6 +9,7 @@
9
9
  * 路徑對不對是使用者一眼就能判斷的事(「那不是我的 Claude Code 裝的地方」),
10
10
  * 但前提是我們得把路徑印出來。
11
11
  */
12
+ import { spawnSync } from "node:child_process";
12
13
  import { existsSync, readdirSync, statSync } from "node:fs";
13
14
  import { homedir, platform, release, userInfo } from "node:os";
14
15
  import { join } from "node:path";
@@ -87,6 +88,26 @@ export async function doctor() {
87
88
  out.push(info(`憑證主儲存 ${isMac ? "macOS Keychain(檔案只是副本,可能過時)" : "檔案"}`));
88
89
  out.push(info(`憑證檔 ${cp} -> ${existsSync(cp) ? `存在 ${statSync(cp).size} bytes` : "不存在"}` +
89
90
  (!existsSync(cp) && !isMac ? " <- 這個平台只有檔案這條路,所以是問題" : "")));
91
+ // ── Windows:PowerShell 執行原則 ──
92
+ // 這個問題有個特別惡劣的性質:**它發生時我們的程式碼根本沒機會執行** ——
93
+ // PowerShell 在 Node 啟動前就拒絕載入 claude-usage.ps1。所以工具無法在出錯當下
94
+ // 自我診斷,只能在 postinstall 事先講、或在這裡(使用者改用 .cmd 跑得起來時)補講。
95
+ if (platform() === "win32") {
96
+ const r = spawnSync("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", "Get-ExecutionPolicy"], { encoding: "utf8", timeout: 10_000, windowsHide: true });
97
+ const policy = r.error || r.status !== 0 ? null : (r.stdout ?? "").trim();
98
+ if (!policy) {
99
+ out.push(info("執行原則 查不到(powershell.exe 不可用?)"));
100
+ }
101
+ else if (policy === "Restricted" || policy === "AllSigned") {
102
+ out.push(bad(`執行原則 ${policy} —— PowerShell 會擋掉 claude-usage.ps1 啟動器`));
103
+ problems.push(`PowerShell 執行原則是 ${policy}(錯誤含 UnauthorizedAccess / ExecutionPolicy)。` +
104
+ `擇一:Set-ExecutionPolicy -Scope CurrentUser RemoteSigned(免管理員),` +
105
+ `或直接用 claude-usage.cmd。公司 GPO 鎖機器層級時只能用後者`);
106
+ }
107
+ else {
108
+ out.push(ok(`執行原則 ${policy}(不會擋 .ps1 啟動器)`));
109
+ }
110
+ }
90
111
  // ── Windows 桌面版(MSIX)──
91
112
  // 只用桌面版、沒登入過 CLI 的人,.credentials.json 裡根本不會有 claudeAiOauth。
92
113
  // 這一段把「桌面版憑證庫在哪、有沒有東西」攤開,否則診斷又會退回「猜 + 手寫指令」。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tsa-group/claude-usage",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
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": {
@@ -8,7 +8,8 @@
8
8
  },
9
9
  "files": [
10
10
  "dist",
11
- "README.md"
11
+ "README.md",
12
+ "postinstall.mjs"
12
13
  ],
13
14
  "engines": {
14
15
  "node": ">=20"
@@ -17,7 +18,8 @@
17
18
  "build": "tsc -p tsconfig.json",
18
19
  "test": "node --test test/*.test.ts",
19
20
  "prepublishOnly": "npm run build && npm test",
20
- "check": "tsc -p tsconfig.json --noEmit"
21
+ "check": "tsc -p tsconfig.json --noEmit",
22
+ "postinstall": "node postinstall.mjs"
21
23
  },
22
24
  "keywords": [
23
25
  "claude",
@@ -0,0 +1,59 @@
1
+ /**
2
+ * 安裝後提示:Windows 的 PowerShell 執行原則會擋掉 npm 產生的 .ps1 啟動器。
3
+ *
4
+ * ★ 為什麼一定要在這裡講:這個失敗發生時**我們的程式碼根本沒機會執行** ——
5
+ * PowerShell 在 Node 啟動前就拒絕載入 `claude-usage.ps1` 了。所以工具永遠無法在
6
+ * 出錯當下自我診斷,提示只能前置,而 postinstall 是唯一「撞牆之前還能說話」的
7
+ * 時機(它由 Node 執行,不受 .ps1 原則管制)。
8
+ *
9
+ * ★ 為什麼要先偵測而不是無條件印:無條件每次安裝都印一段警告,就是在訓練人忽略
10
+ * 它 —— 等真的出事那次也會被跳過。只在**實際會擋**的機器上印,訊息才有訊號價值。
11
+ *
12
+ * ★ 為什麼這支檔案刻意獨立、不 import dist/:postinstall 失敗會讓整個安裝失敗。
13
+ * 它必須在 dist 壞掉、Node 版本奇怪、或任何預期外的狀況下都安靜地成功。
14
+ * 與 doctor 裡那份偵測有少量重複,那是刻意的隔離。
15
+ */
16
+ import { spawnSync } from "node:child_process";
17
+
18
+ // 這兩種原則會擋掉 .ps1 啟動器。RemoteSigned / Unrestricted / Bypass 都不會。
19
+ const BLOCKING = new Set(["Restricted", "AllSigned"]);
20
+
21
+ function effectivePolicy() {
22
+ const r = spawnSync("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", "Get-ExecutionPolicy"], {
23
+ encoding: "utf8",
24
+ timeout: 10_000,
25
+ windowsHide: true,
26
+ });
27
+ if (r.error || r.status !== 0) return null;
28
+ return (r.stdout ?? "").trim() || null;
29
+ }
30
+
31
+ try {
32
+ if (process.platform === "win32") {
33
+ const policy = effectivePolicy();
34
+ if (policy && BLOCKING.has(policy)) {
35
+ const L = [
36
+ "",
37
+ ` ⚠ PowerShell 執行原則是 ${policy} —— 在 PowerShell 直接打 claude-usage 會被擋`,
38
+ " (錯誤訊息含 UnauthorizedAccess / PSSecurityException / ExecutionPolicy)",
39
+ "",
40
+ " 擇一處理:",
41
+ " 1) 設定一次、永久生效、免系統管理員:",
42
+ " Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned",
43
+ " RemoteSigned = 本機與 npm 的腳本可跑,只擋網路下載的未簽章腳本。",
44
+ " 2) 不改原則,改叫 .cmd 啟動器(功能完全一樣):",
45
+ " claude-usage.cmd status",
46
+ "",
47
+ " 公司用 GPO 鎖了機器層級原則時,(1) 會被覆寫 —— 直接用 (2)。",
48
+ " 現況查詢: Get-ExecutionPolicy -List",
49
+ "",
50
+ ];
51
+ console.log(L.join("\n"));
52
+ }
53
+ }
54
+ } catch {
55
+ // 提示壞掉絕不能讓安裝失敗
56
+ }
57
+
58
+ // 無論如何都成功收場
59
+ process.exitCode = 0;