@usagefleet/cli 1.2.141 → 1.2.149

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/collector.js CHANGED
@@ -12,7 +12,7 @@ import { listJsonlFiles } from './scanner.js';
12
12
  import { readStore, updateStore } from './store.js';
13
13
  import { tailFile } from './tailer.js';
14
14
  import { tilde } from './ui.js';
15
- import { postLimits, uploadBatch } from './uploader.js';
15
+ import { fetchGroupLimits, postLimits, uploadBatch } from './uploader.js';
16
16
  /** Only files inside a `.../.claude/projects/...` subtree are real usage logs.
17
17
  * Desktop session roots also hold `audit.jsonl` (a full duplicate of the same
18
18
  * uuids) and other JSONL — restricting to this subtree mirrors Claude Code and
@@ -274,8 +274,15 @@ export async function reportLimitsOnce(cfg, log = () => {
274
274
  source: report.source,
275
275
  };
276
276
  });
277
- // Local desktop notification on freshly-crossed thresholds. Independent of the
278
- // server upload (notify even if the POST failed) and never throws.
279
- maybeNotify(report, undefined, log);
277
+ // Local desktop notification on freshly-crossed thresholds, judged on the
278
+ // device's group slice because that is the budget it is held to. Only a device
279
+ // without a group falls back to the account reading. No answer from the server
280
+ // means no notification rather than one on the wrong number.
281
+ const groupLimits = await fetchGroupLimits(cfg, account?.extId);
282
+ if (groupLimits) {
283
+ maybeNotify(groupLimits.group === null
284
+ ? report
285
+ : { ...report, fiveHourPct: groupLimits.sessionPct, sevenDayPct: groupLimits.weeklyPct }, undefined, log);
286
+ }
280
287
  return report;
281
288
  }
package/dist/guard.js CHANGED
@@ -15,7 +15,8 @@ export function blockMessage(view) {
15
15
  }
16
16
  const window = view.blockedWindow === 'weekly' ? 'weekly' : '5h';
17
17
  const limit = view.blockedModel ? `${view.blockedModel} ${window}` : window;
18
- const pct = view.blockedPct ?? 100;
18
+ const s = (view.blockedPct ?? 100).toFixed(1);
19
+ const pct = s.endsWith('.0') ? s.slice(0, -2) : s;
19
20
  const group = view.group ? `"${view.group}"` : 'this group';
20
21
  const until = view.blockedUntil ? new Date(view.blockedUntil) : null;
21
22
  const resets = until && !Number.isNaN(until.getTime())
package/dist/hook.js CHANGED
@@ -19,7 +19,9 @@ const GUARD_COMMAND = /usagefleet.*\bguard\b/;
19
19
  * shells can be tested; cmd.exe has no inline `VAR=value cmd` form, and the
20
20
  * POSIX prefix there would be read as a program name and never launch. */
21
21
  export function guardCommand(program, configPath, platform = process.platform) {
22
- const quote = (p) => (p.includes(' ') ? `"${p}"` : p);
22
+ // Claude Code on Windows runs hooks through Git Bash, which strips the backslashes
23
+ // from an unquoted `C:\Users\...\index.js`; node then exits 1 and the prompt passes.
24
+ const quote = (p) => (p.includes(' ') || p.includes('\\') ? `"${p}"` : p);
23
25
  const command = program.map(quote).join(' ');
24
26
  if (!configPath) {
25
27
  return command;
package/dist/index.js CHANGED
@@ -29,7 +29,7 @@ function flag(name) {
29
29
  }
30
30
  return undefined;
31
31
  }
32
- /** "5h ██░░░░░░░░ 2% · weekly ████░░░░░░ 13%" — the shared limits line.
32
+ /** "5h ██░░░░░░░░ 2.4% · weekly ████░░░░░░ 13.0%" — the shared limits line.
33
33
  * Bars are plain characters, so they survive a service log as well as a TTY. */
34
34
  function limitsSummary(limits) {
35
35
  const models = limits.modelLimits.map(m => ` · ${m.model}(${m.window}) ${bar(m.pct, 6)} ${pct(m.pct)}`).join('');
package/dist/notifier.js CHANGED
@@ -80,9 +80,12 @@ export function maybeNotify(report, cfg = loadNotifyConfig(), log = () => {
80
80
  const state = readStore(path).notify;
81
81
  const five = evaluateWindow(state.fiveHour, report.fiveHourPct, report.fiveHourResetsAt, cfg.thresholds);
82
82
  const seven = evaluateWindow(state.sevenDay, report.sevenDayPct, report.sevenDayResetsAt, cfg.thresholds);
83
- // Readings can carry a decimal; notification copy rounds to whole.
84
- const fivePct = Math.round(report.fiveHourPct ?? 0);
85
- const sevenPct = Math.round(report.sevenDayPct ?? 0);
83
+ const fmt = (n) => {
84
+ const s = n.toFixed(1);
85
+ return s.endsWith('.0') ? s.slice(0, -2) : s;
86
+ };
87
+ const fivePct = fmt(report.fiveHourPct ?? 0);
88
+ const sevenPct = fmt(report.sevenDayPct ?? 0);
86
89
  if (five.fire != null) {
87
90
  sendNotification('Claude usage · 5-hour limit', `${fivePct}% of your 5-hour limit used${resetSuffix(report.fiveHourResetsAt)}.`, { urgency: urgencyFor(five.fire) });
88
91
  log('ok', `notified · 5h at ${fivePct}% · crossed ${five.fire}%`);
package/dist/release.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // Generated by .github/workflows/release.yml.
2
- export const RELEASE_VERSION = "1.2.141";
2
+ export const RELEASE_VERSION = "1.2.149";
package/dist/ui.js CHANGED
@@ -86,10 +86,15 @@ export function line(glyph, text) {
86
86
  }
87
87
  /** Neutral stream glyph, for messages that are neither good nor bad news. */
88
88
  export const note = dim('·');
89
- /** Percentage as a fixed-width string, so successive log lines line up.
90
- * Readings can carry a decimal; display rounds to whole. */
89
+ /** Percentage with one decimal, fixed-width so successive log lines line up
90
+ * (wide enough for "100.0%"). */
91
91
  export function pct(value) {
92
- return `${value === null ? '?' : Math.round(value)}%`.padStart(4);
92
+ if (value === null) {
93
+ return '?'.padStart(6);
94
+ }
95
+ const s = value.toFixed(1);
96
+ const str = s.endsWith('.0') ? s.slice(0, -2) : s;
97
+ return `${str}%`.padStart(6);
93
98
  }
94
99
  /** Usage bar, coloured by how close the window is to its limit.
95
100
  * Unknown usage renders as an empty bar rather than a missing column. */
package/dist/uploader.js CHANGED
@@ -101,3 +101,23 @@ export async function postLimits(report, cfg) {
101
101
  return 'transient';
102
102
  }
103
103
  }
104
+ /** Null when the server cannot answer (offline, rejected, older than this field). */
105
+ export async function fetchGroupLimits(cfg, accountExtId) {
106
+ try {
107
+ const res = await fetch(`${ENDPOINT}/api/v1/limits`, {
108
+ headers: { 'x-api-key': tokenFor(cfg, accountExtId) },
109
+ signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
110
+ });
111
+ if (!res.ok) {
112
+ return null;
113
+ }
114
+ const body = (await res.json());
115
+ if (typeof body?.sessionPct !== 'number' || typeof body.weeklyPct !== 'number') {
116
+ return null;
117
+ }
118
+ return { group: body.group ?? null, sessionPct: body.sessionPct, weeklyPct: body.weeklyPct };
119
+ }
120
+ catch {
121
+ return null;
122
+ }
123
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usagefleet/cli",
3
- "version": "1.2.141",
3
+ "version": "1.2.149",
4
4
  "description": "Tails Claude Code, Claude Desktop, and pi agent JSONL logs and reports token usage to a UsageFleet server.",
5
5
  "keywords": [
6
6
  "claude",