@usagefleet/cli 1.2.184 → 1.2.186

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
@@ -118,6 +118,8 @@ WinRT toast via `powershell.exe`.
118
118
  `usagefleet notify-test` confirms it works and prints the thresholds in effect.
119
119
  For a list of your own use `USAGEFLEET_NOTIFY_THRESHOLDS="50,80,99.5"` (it
120
120
  replaces the mode's list), disable everything with `USAGEFLEET_NOTIFY=0`.
121
+ To also hear when a window that crossed a threshold resets, set
122
+ `USAGEFLEET_NOTIFY_RESET=1` or `"notifyReset": true`.
121
123
 
122
124
  ## Blocking prompts over the limit
123
125
 
@@ -180,6 +182,7 @@ it (`usagefleet config` prints this same table):
180
182
  | `limitsInterval` | `USAGEFLEET_LIMITS_INTERVAL` | seconds between limit reports (default 60; 300 on API keys, where each report costs a 1-token ping) |
181
183
  | `notifications` | `USAGEFLEET_NOTIFY` | desktop notifications, on by default (`false`/`0` disables) |
182
184
  | `notifyMode` | `USAGEFLEET_NOTIFY_MODE` | alert preset: `off`, `quiet`, `standard`, `steady`, `critical` (default: the Devices page value, else `standard`) |
185
+ | `notifyReset` | `USAGEFLEET_NOTIFY_RESET` | also notify when a window that crossed a threshold resets, off by default (`true`/`1` enables) |
183
186
  | `notifyThresholds` | `USAGEFLEET_NOTIFY_THRESHOLDS` | utilization % that trigger an alert, replacing the mode's list — array in the file, comma list in env, one decimal kept |
184
187
  | `batch` | `USAGEFLEET_BATCH` | records per upload (default 100, server caps at 1000) |
185
188
  | `update` | `USAGEFLEET_UPDATE` | `false`/`0` turns the self-update check off |
package/dist/index.js CHANGED
@@ -175,7 +175,7 @@ function cmdNotifyTest() {
175
175
  sendNotification('usagefleet', 'Test notification — desktop alerts are working.', {
176
176
  urgency: 'normal',
177
177
  });
178
- console.log(step('notified', `${detectOs()} · thresholds ${cfg.thresholds.join(', ')}%`));
178
+ console.log(step('notified', `${detectOs()} · thresholds ${cfg.thresholds.join(', ')}%${cfg.notifyReset ? ' · on reset' : ''}`));
179
179
  }
180
180
  async function cmdStatus() {
181
181
  const cfg = loadConfig();
@@ -298,6 +298,7 @@ function cmdConfig() {
298
298
  ['batch', 'USAGEFLEET_BATCH', 'records per upload (default 100, max 1000)'],
299
299
  ['notifications', 'USAGEFLEET_NOTIFY', 'desktop notifications (false/0 disables)'],
300
300
  ['notifyMode', 'USAGEFLEET_NOTIFY_MODE', 'alert preset: off | quiet | standard | steady | critical'],
301
+ ['notifyReset', 'USAGEFLEET_NOTIFY_RESET', 'also alert when a window that crossed a threshold resets'],
301
302
  ['notifyThresholds', 'USAGEFLEET_NOTIFY_THRESHOLDS', '% alerts · array; env comma list (replaces the preset)'],
302
303
  ['update', 'USAGEFLEET_UPDATE', 'self-update while watching (false/0 disables)'],
303
304
  ['updateInterval', 'USAGEFLEET_UPDATE_INTERVAL', 'seconds between update checks (default 21600)'],
package/dist/notifier.js CHANGED
@@ -36,7 +36,9 @@ export function loadNotifyConfig(env = process.env, file = readStore(), serverMo
36
36
  // One decimal, like the readings themselves: 99.1 must stay 99.1.
37
37
  const parsed = candidates.map(n => Math.round(n * 10) / 10).filter(n => Number.isFinite(n) && n > 0 && n <= 100);
38
38
  const thresholds = parsed.length > 0 ? [...new Set(parsed)].toSorted((a, b) => a - b) : NOTIFY_MODES[mode];
39
- return { enabled, thresholds };
39
+ const rawReset = env.USAGEFLEET_NOTIFY_RESET;
40
+ const notifyReset = rawReset?.trim() ? !flagOff(rawReset) : file.notifyReset === true;
41
+ return { enabled, notifyReset, thresholds };
40
42
  }
41
43
  /**
42
44
  * Decide whether a window crosses a not-yet-notified threshold. Pure — no IO.
@@ -48,15 +50,21 @@ export function loadNotifyConfig(env = process.env, file = readStore(), serverMo
48
50
  * is lowered so a subsequent re-cross notifies again. A small dip does not move
49
51
  * it: a group's slice is derived and wobbles between readings, and a mark that
50
52
  * followed every wobble would re-alert each time pct straddled a threshold.
53
+ *
54
+ * `reset` is true once, on the first reading after a window that crossed a
55
+ * threshold has passed its reset time.
51
56
  */
52
- export function evaluateWindow(prev, pct, resetsAt, thresholds) {
57
+ export function evaluateWindow(prev, pct, resetsAt, thresholds, now = Date.now()) {
53
58
  const rolledOver = !prev || !sameWindow(prev.resetsAt, resetsAt);
54
59
  const lastBucket = rolledOver ? 0 : prev.lastBucket;
55
60
  // Within a window the first resetsAt stays, so the jitter alone never rewrites the store.
56
61
  const windowResetsAt = rolledOver ? resetsAt : prev.resetsAt;
62
+ // The time check matters: an idle account reads resets_at null, and the next window
63
+ // starting from null is a rollover too, but not a reset.
64
+ const reset = rolledOver && !!prev && prev.lastBucket > 0 && prev.resetsAt !== null && Date.parse(prev.resetsAt) <= now;
57
65
  if (pct == null) {
58
66
  // No reading this cycle — keep the mark, just track the (possibly new) window.
59
- return { fire: null, urgent: false, next: { lastBucket, resetsAt: windowResetsAt } };
67
+ return { fire: null, urgent: false, reset, next: { lastBucket, resetsAt: windowResetsAt } };
60
68
  }
61
69
  // Highest threshold the current pct has reached (thresholds are ascending).
62
70
  let top = 0;
@@ -69,12 +77,12 @@ export function evaluateWindow(prev, pct, resetsAt, thresholds) {
69
77
  // `critical` is sticky on most Linux daemons, so only entering the danger zone and
70
78
  // the wall get it; a dense tail (99.1, 99.2, ...) must not pile up.
71
79
  const urgent = (lastBucket < DANGER_ZONE && top >= DANGER_ZONE) || top >= 100;
72
- return { fire: top, urgent, next: { lastBucket: top, resetsAt: windowResetsAt } };
80
+ return { fire: top, urgent, reset, next: { lastBucket: top, resetsAt: windowResetsAt } };
73
81
  }
74
82
  // top <= lastBucket: nothing fires. The mark holds through a wobble and follows
75
83
  // pct down only on a real drop.
76
84
  const mark = pct < lastBucket - REARM_MARGIN ? top : lastBucket;
77
- return { fire: null, urgent: false, next: { lastBucket: mark, resetsAt: windowResetsAt } };
85
+ return { fire: null, urgent: false, reset, next: { lastBucket: mark, resetsAt: windowResetsAt } };
78
86
  }
79
87
  // oauth/usage jitters resets_at by milliseconds per request; a real rollover moves it by hours.
80
88
  function sameWindow(a, b) {
@@ -123,6 +131,14 @@ export function maybeNotify(report, cfg = loadNotifyConfig(), log = () => {
123
131
  };
124
132
  const fivePct = fmt(report.fiveHourPct ?? 0);
125
133
  const sevenPct = fmt(report.sevenDayPct ?? 0);
134
+ if (cfg.notifyReset && five.reset) {
135
+ sendNotification('Claude usage · 5-hour limit', 'Your 5-hour limit has reset.');
136
+ log('ok', 'notified · 5h window reset');
137
+ }
138
+ if (cfg.notifyReset && seven.reset) {
139
+ sendNotification('Claude usage · weekly limit', 'Your weekly limit has reset.');
140
+ log('ok', 'notified · weekly window reset');
141
+ }
126
142
  if (five.fire != null) {
127
143
  sendNotification('Claude usage · 5-hour limit', `${fivePct}% of your 5-hour limit used${resetSuffix(report.fiveHourResetsAt)}.`, { urgency: five.urgent ? 'critical' : 'normal' });
128
144
  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.184";
2
+ export const RELEASE_VERSION = "1.2.186";
package/dist/store.js CHANGED
@@ -61,6 +61,7 @@ function normalize(raw) {
61
61
  sevenDay: { ...freshWindow(), ...raw.notify?.sevenDay },
62
62
  },
63
63
  notifyMode: raw.notifyMode,
64
+ notifyReset: raw.notifyReset,
64
65
  notifyThresholds: raw.notifyThresholds,
65
66
  piDir: raw.piDir,
66
67
  pjDir: raw.pjDir,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usagefleet/cli",
3
- "version": "1.2.184",
3
+ "version": "1.2.186",
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",