ccusage-tracker 0.1.3 → 0.1.6

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 (3) hide show
  1. package/README.md +1 -1
  2. package/dist/index.js +56 -10
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -30,7 +30,7 @@ After installation, the binary is also available as `tracker` (if installed glob
30
30
  - **Stop** — primary reporting path: POSTs token usage + session metrics after each assistant turn, throttled to once per 5 minutes
31
31
  - **SessionEnd** — backup path: same payload at session exit, in case Stop missed the last window
32
32
 
33
- Hook scripts are downloaded from your tracker server, so they always match the server version. Re-running `setup` migrates old (no `--mode`) commands in-place — no manual cleanup needed.
33
+ Hook scripts are downloaded from your tracker server, so they always match the server version. Re-running `setup` migrates old (no `--mode`) commands and unquoted tracker hook paths in-place — no manual cleanup needed.
34
34
 
35
35
  ## Requirements
36
36
 
package/dist/index.js CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  // src/commands/setup.ts
4
4
  import { createInterface } from "node:readline";
5
+ import { spawnSync } from "node:child_process";
5
6
 
6
7
  // src/config.ts
7
8
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
@@ -36,21 +37,28 @@ function writeConfig(config) {
36
37
  import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2, copyFileSync, mkdirSync as mkdirSync2 } from "node:fs";
37
38
  import { join as join2 } from "node:path";
38
39
  import { homedir as homedir2 } from "node:os";
39
- var HOOK_TIMEOUT_SEC = 25;
40
+ var HOOK_TIMEOUT_SEC = 45;
40
41
  function getClaudeSettingsPath() {
41
42
  return join2(homedir2(), ".claude", "settings.json");
42
43
  }
43
44
  function sessionEndScriptPath() {
44
45
  return join2(homedir2(), ".config", "ccusage-tracker", "session-end.mjs");
45
46
  }
47
+ function quoteScriptPath(path) {
48
+ return `"${path}"`;
49
+ }
50
+ function buildHookCommand(scriptPath, mode) {
51
+ const command = "node " + quoteScriptPath(scriptPath);
52
+ return mode ? `${command} --mode=${mode}` : command;
53
+ }
46
54
  function getHookCommand() {
47
- return "node " + sessionEndScriptPath() + " --mode=session-end";
55
+ return buildHookCommand(sessionEndScriptPath(), "session-end");
48
56
  }
49
57
  function getStopHookCommand() {
50
- return "node " + sessionEndScriptPath() + " --mode=stop";
58
+ return buildHookCommand(sessionEndScriptPath(), "stop");
51
59
  }
52
60
  function getStartHookCommand() {
53
- return "node " + join2(homedir2(), ".config", "ccusage-tracker", "session-start.mjs");
61
+ return buildHookCommand(join2(homedir2(), ".config", "ccusage-tracker", "session-start.mjs"));
54
62
  }
55
63
  function isCcusageTrackerHook(command) {
56
64
  return !!command && command.includes("ccusage-tracker");
@@ -179,8 +187,7 @@ async function defaultCheckServer(serverUrl) {
179
187
  }
180
188
  function defaultCheckCcusage() {
181
189
  try {
182
- Bun.spawnSync(["ccusage", "--version"]);
183
- return true;
190
+ return spawnSync("ccusage --version", { encoding: "utf8", shell: true, timeout: 1e4 }).status === 0;
184
191
  } catch {
185
192
  return false;
186
193
  }
@@ -363,6 +370,7 @@ async function reportCommand(args) {
363
370
 
364
371
  // src/commands/status.ts
365
372
  import { existsSync as existsSync3, readFileSync as readFileSync3 } from "node:fs";
373
+ import { spawnSync as spawnSync2 } from "node:child_process";
366
374
  import { join as join3, dirname } from "node:path";
367
375
  async function statusCommand() {
368
376
  const configPath = getConfigPath();
@@ -408,12 +416,50 @@ Hook: ${hookInstalled ? "installed" : "not installed"}`);
408
416
  } else {
409
417
  console.log("Buffer: none");
410
418
  }
419
+ const configDir = dirname(configPath);
420
+ const lastErrorPath = join3(configDir, "last-error.txt");
421
+ if (existsSync3(lastErrorPath)) {
422
+ const reason = readFileSync3(lastErrorPath, "utf-8").trim();
423
+ console.log(`
424
+ Last upload: FAILED - ${reason}`);
425
+ console.log(uploadFailureHint(reason));
426
+ } else {
427
+ console.log(`
428
+ Last upload: no recorded failure`);
429
+ }
430
+ const lastFlushPath = join3(configDir, "last-flush.txt");
431
+ if (existsSync3(lastFlushPath)) {
432
+ const ts = parseInt(readFileSync3(lastFlushPath, "utf-8").trim(), 10);
433
+ if (!isNaN(ts)) {
434
+ const ageMin = Math.floor((Date.now() - ts) / 60000);
435
+ console.log(`Last hook run: ${new Date(ts).toISOString()} (${ageMin} 分鐘前)`);
436
+ }
437
+ }
438
+ const probe = probeCcusage();
439
+ console.log(probe ? `ccusage: installed (${probe})` : "ccusage: not found (install with: npx ccusage@latest)");
440
+ }
441
+ function uploadFailureHint(reason) {
442
+ if (reason.includes("ccusage")) {
443
+ return [
444
+ " 用量目前並未上報。請量測 ccusage 耗時,若接近 25s 需再放寬 timeout:",
445
+ " time ccusage daily --json --since $(date +%Y%m%d)"
446
+ ].join(`
447
+ `);
448
+ }
449
+ return [
450
+ " 用量目前並未上報。ccusage 有取到數,卡在送出那一段 —— 先看上面的 Server 一行,",
451
+ " 再檢查網路 / VPN / proxy。當日快照是整日累計,下次 hook 跑成就會補齊"
452
+ ].join(`
453
+ `);
454
+ }
455
+ function probeCcusage() {
411
456
  try {
412
- const result = Bun.spawnSync(["ccusage", "--version"]);
413
- const version = new TextDecoder().decode(result.stdout).trim();
414
- console.log(`ccusage: installed (${version || "version unknown"})`);
457
+ const r = spawnSync2("ccusage --version", { encoding: "utf8", shell: true, timeout: 1e4 });
458
+ if (r.status !== 0 || !r.stdout)
459
+ return null;
460
+ return r.stdout.trim() || "version unknown";
415
461
  } catch {
416
- console.log("ccusage: not found (install with: npx ccusage@latest)");
462
+ return null;
417
463
  }
418
464
  }
419
465
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccusage-tracker",
3
- "version": "0.1.3",
3
+ "version": "0.1.6",
4
4
  "description": "CLI for ccusage-tracker — install Claude Code SessionStart/SessionEnd/Stop hooks to report token usage to a self-hosted team tracker.",
5
5
  "type": "module",
6
6
  "bin": {