@usagefleet/cli 1.2.171 → 1.2.173
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 +20 -6
- package/dist/claude-account.js +28 -0
- package/dist/collector.js +35 -18
- package/dist/index.js +3 -2
- package/dist/notifier.js +46 -18
- package/dist/release.js +1 -1
- package/dist/store.js +1 -0
- package/dist/uploader.js +7 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -97,15 +97,28 @@ directory, git branch. Prompts, responses and file contents are never read.
|
|
|
97
97
|
|
|
98
98
|
## Desktop notifications
|
|
99
99
|
|
|
100
|
-
The first time a window crosses a threshold
|
|
101
|
-
|
|
102
|
-
|
|
100
|
+
The first time a window crosses a threshold you get a desktop notification —
|
|
101
|
+
once per threshold per window, re-armed when the window resets or usage falls
|
|
102
|
+
5 points under the last alert. Which thresholds is the alert **mode**:
|
|
103
|
+
|
|
104
|
+
| Mode | Alerts at (% of the window) |
|
|
105
|
+
|------|-----------------------------|
|
|
106
|
+
| `off` | never |
|
|
107
|
+
| `quiet` | 95, 100 |
|
|
108
|
+
| `standard` (default) | 80, 95 |
|
|
109
|
+
| `steady` | 50, 60, 70, 80, 90, 95, 100 |
|
|
110
|
+
| `critical` | 80, 90, 95, 96, 97, 98, 99, then every 0.1 (99.1, 99.2 … 99.9), 100 |
|
|
111
|
+
|
|
112
|
+
Pick one per device on the **Devices** page, or pin it on the machine with
|
|
113
|
+
`USAGEFLEET_NOTIFY_MODE=critical` / `"notifyMode": "critical"` in the config
|
|
114
|
+
file (the local value wins over the Devices page). macOS shows it from a small bundled UsageFleet app (allow it once when
|
|
103
115
|
macOS asks; a click opens the dashboard), Linux uses `notify-send` (falling back to
|
|
104
116
|
`kdialog --passivepopup`; install `libnotify-bin` if neither exists), Windows a
|
|
105
117
|
WinRT toast via `powershell.exe`.
|
|
106
118
|
|
|
107
|
-
`usagefleet notify-test` confirms it works
|
|
108
|
-
`USAGEFLEET_NOTIFY_THRESHOLDS="50,80,
|
|
119
|
+
`usagefleet notify-test` confirms it works and prints the thresholds in effect.
|
|
120
|
+
For a list of your own use `USAGEFLEET_NOTIFY_THRESHOLDS="50,80,99.5"` (it
|
|
121
|
+
replaces the mode's list), disable everything with `USAGEFLEET_NOTIFY=0`.
|
|
109
122
|
|
|
110
123
|
## Blocking prompts over the limit
|
|
111
124
|
|
|
@@ -165,7 +178,8 @@ it (`usagefleet config` prints this same table):
|
|
|
165
178
|
| `interval` | `USAGEFLEET_INTERVAL` | watch poll seconds (default 15) |
|
|
166
179
|
| `limitsInterval` | `USAGEFLEET_LIMITS_INTERVAL` | seconds between limit reports (default 60; 300 on API keys, where each report costs a 1-token ping) |
|
|
167
180
|
| `notifications` | `USAGEFLEET_NOTIFY` | desktop notifications, on by default (`false`/`0` disables) |
|
|
168
|
-
| `
|
|
181
|
+
| `notifyMode` | `USAGEFLEET_NOTIFY_MODE` | alert preset: `off`, `quiet`, `standard`, `steady`, `critical` (default: the Devices page value, else `standard`) |
|
|
182
|
+
| `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 |
|
|
169
183
|
| `batch` | `USAGEFLEET_BATCH` | records per upload (default 100, server caps at 1000) |
|
|
170
184
|
| `update` | `USAGEFLEET_UPDATE` | `false`/`0` turns the self-update check off |
|
|
171
185
|
| `updateInterval` | `USAGEFLEET_UPDATE_INTERVAL` | seconds between update checks (default `21600` = 6h) |
|
package/dist/claude-account.js
CHANGED
|
@@ -46,6 +46,34 @@ export function activeJcodeToken(authPath) {
|
|
|
46
46
|
return null;
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
|
+
/** Every Claude subscription login saved in the agent CLI's `accounts.json` whose access
|
|
50
|
+
* token has not expired; the agent refreshes a token only when it uses that account. */
|
|
51
|
+
export function agentClaudeLogins(accountsPath) {
|
|
52
|
+
let accounts;
|
|
53
|
+
try {
|
|
54
|
+
accounts = JSON.parse(readFileSync(accountsPath, 'utf-8')).anthropic ?? [];
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return [];
|
|
58
|
+
}
|
|
59
|
+
const logins = [];
|
|
60
|
+
for (const { type, name, access, accountId, expires } of accounts) {
|
|
61
|
+
if (type === 'oauth' &&
|
|
62
|
+
typeof access === 'string' &&
|
|
63
|
+
access !== '' &&
|
|
64
|
+
typeof accountId === 'string' &&
|
|
65
|
+
accountId !== '' &&
|
|
66
|
+
typeof expires === 'number' &&
|
|
67
|
+
expires > Date.now()) {
|
|
68
|
+
logins.push({
|
|
69
|
+
extId: accountId.slice(0, 100),
|
|
70
|
+
name: typeof name === 'string' ? name : accountId,
|
|
71
|
+
token: access,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return logins;
|
|
76
|
+
}
|
|
49
77
|
/** The accountUuid Anthropic says owns this OAuth token, or null when it cannot
|
|
50
78
|
* say. Null fails open to the file's identity: the endpoint is undocumented, and
|
|
51
79
|
* losing it must not stop every collector from reporting limits. */
|
package/dist/collector.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
2
|
import { hostname } from 'node:os';
|
|
3
3
|
import { dirname, join, sep } from 'node:path';
|
|
4
|
-
import { activeJcodeToken, detectClaudeAccount, tokenOwner } from './claude-account.js';
|
|
4
|
+
import { activeJcodeToken, agentClaudeLogins, detectClaudeAccount, tokenOwner } from './claude-account.js';
|
|
5
5
|
import { detectClaudeCreds, macKeychainDenied } from './claude-creds.js';
|
|
6
6
|
import { fetchLimits } from './claude-limits.js';
|
|
7
7
|
import { ENDPOINT } from './config.js';
|
|
8
8
|
import { listJcodeSessions, readJcodeSession } from './jcode.js';
|
|
9
|
-
import { maybeNotify } from './notifier.js';
|
|
9
|
+
import { loadNotifyConfig, maybeNotify } from './notifier.js';
|
|
10
10
|
import { detectOs } from './os.js';
|
|
11
11
|
import { RELEASE_VERSION } from './release.js';
|
|
12
12
|
import { listJsonlFiles } from './scanner.js';
|
|
@@ -324,29 +324,23 @@ function pruneMissingFiles(state, scanned) {
|
|
|
324
324
|
function planWall() {
|
|
325
325
|
return `device outside your plan's device limit · free a slot or upgrade at ${ENDPOINT}/fleet · nothing is lost, uploads resume once it fits`;
|
|
326
326
|
}
|
|
327
|
-
/** Report the Claude Code login's limits, then those of
|
|
327
|
+
/** Report the Claude Code login's limits, then those of every other account jcode, pj or the agent CLI is signed in to. */
|
|
328
328
|
export async function reportLimitsOnce(cfg, log = () => {
|
|
329
329
|
/* empty */
|
|
330
330
|
}) {
|
|
331
331
|
const report = await reportClaudeCodeLimits(cfg, log);
|
|
332
|
-
await
|
|
332
|
+
await reportOtherLogins(cfg, report?.account?.extId, log);
|
|
333
333
|
return report;
|
|
334
334
|
}
|
|
335
335
|
/**
|
|
336
|
-
* jcode and
|
|
336
|
+
* jcode, pj and the agent CLI keep their own Anthropic logins, so the account they use
|
|
337
337
|
* can differ from Claude Code's; with no reading of its own that account's limits go
|
|
338
338
|
* stale on the server and its group stops blocking.
|
|
339
339
|
*/
|
|
340
|
-
async function
|
|
340
|
+
async function reportOtherLogins(cfg, reportedExtId, log) {
|
|
341
341
|
const reported = new Set(reportedExtId ? [reportedExtId] : []);
|
|
342
|
-
for (const
|
|
343
|
-
if (
|
|
344
|
-
continue;
|
|
345
|
-
}
|
|
346
|
-
// Never refreshed here: rotating the refresh token would sign jcode or pj out.
|
|
347
|
-
const token = activeJcodeToken(join(dirname(sessionsDir), 'auth.json'));
|
|
348
|
-
const extId = token ? await tokenOwner(token) : null;
|
|
349
|
-
if (!token || !extId || reported.has(extId)) {
|
|
342
|
+
for (const { extId, label, token } of await otherLogins(cfg)) {
|
|
343
|
+
if (reported.has(extId)) {
|
|
350
344
|
continue;
|
|
351
345
|
}
|
|
352
346
|
reported.add(extId);
|
|
@@ -355,15 +349,36 @@ async function reportJcodeLimits(cfg, reportedExtId, log) {
|
|
|
355
349
|
report = await fetchLimits({ source: 'sub', token });
|
|
356
350
|
}
|
|
357
351
|
catch (error) {
|
|
358
|
-
log('warn', `limits fetch failed for the ${
|
|
352
|
+
log('warn', `limits fetch failed for the ${label} login · ${error.message}`);
|
|
359
353
|
continue;
|
|
360
354
|
}
|
|
361
355
|
const outcome = await postLimits({ ...report, account: { email: null, extId, org: null } }, cfg);
|
|
362
356
|
if (outcome !== 'ok') {
|
|
363
|
-
log('warn', `limits upload failed for the ${
|
|
357
|
+
log('warn', `limits upload failed for the ${label} login · ${outcome}`);
|
|
364
358
|
}
|
|
365
359
|
}
|
|
366
360
|
}
|
|
361
|
+
// Never refreshed here: rotating a refresh token would sign jcode, pj or the agent out.
|
|
362
|
+
async function otherLogins(cfg) {
|
|
363
|
+
const logins = [];
|
|
364
|
+
for (const sessionsDir of [cfg.jcodeDir, cfg.pjDir]) {
|
|
365
|
+
if (!sessionsDir) {
|
|
366
|
+
continue;
|
|
367
|
+
}
|
|
368
|
+
const token = activeJcodeToken(join(dirname(sessionsDir), 'auth.json'));
|
|
369
|
+
const extId = token ? await tokenOwner(token) : null;
|
|
370
|
+
if (token && extId) {
|
|
371
|
+
logins.push({ extId, label: tilde(dirname(sessionsDir)), token });
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
if (cfg.agentDir) {
|
|
375
|
+
const accountsPath = join(dirname(cfg.agentDir), 'accounts.json');
|
|
376
|
+
for (const login of agentClaudeLogins(accountsPath)) {
|
|
377
|
+
logins.push({ ...login, label: `${tilde(dirname(cfg.agentDir))} ${login.name}` });
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
return logins;
|
|
381
|
+
}
|
|
367
382
|
/**
|
|
368
383
|
* Auto-detect the local Claude login, read the real 5h/weekly utilization
|
|
369
384
|
* (oauth/usage for subscription logins, rate-limit headers for API keys), and
|
|
@@ -446,7 +461,9 @@ async function reportClaudeCodeLimits(cfg, log) {
|
|
|
446
461
|
if (groupLimits) {
|
|
447
462
|
maybeNotify(groupLimits.group === null
|
|
448
463
|
? report
|
|
449
|
-
: { ...report, fiveHourPct: groupLimits.sessionPct, sevenDayPct: groupLimits.weeklyPct },
|
|
464
|
+
: { ...report, fiveHourPct: groupLimits.sessionPct, sevenDayPct: groupLimits.weeklyPct },
|
|
465
|
+
// The device's mode from the Devices page rides along on the same answer.
|
|
466
|
+
loadNotifyConfig(undefined, undefined, groupLimits.notifyMode), log);
|
|
450
467
|
}
|
|
451
|
-
return report;
|
|
468
|
+
return { ...report, account };
|
|
452
469
|
}
|
package/dist/index.js
CHANGED
|
@@ -166,7 +166,7 @@ async function cmdWatch() {
|
|
|
166
166
|
function cmdNotifyTest() {
|
|
167
167
|
const cfg = loadNotifyConfig();
|
|
168
168
|
if (!cfg.enabled) {
|
|
169
|
-
console.log(warn('notify', 'disabled ·
|
|
169
|
+
console.log(warn('notify', 'disabled · mode off, or USAGEFLEET_NOTIFY=0 / `"notifications": false` · see `usagefleet config`'));
|
|
170
170
|
return;
|
|
171
171
|
}
|
|
172
172
|
sendNotification('usagefleet', 'Test notification — desktop alerts are working.', {
|
|
@@ -294,7 +294,8 @@ function cmdConfig() {
|
|
|
294
294
|
],
|
|
295
295
|
['batch', 'USAGEFLEET_BATCH', 'records per upload (default 100, max 1000)'],
|
|
296
296
|
['notifications', 'USAGEFLEET_NOTIFY', 'desktop notifications (false/0 disables)'],
|
|
297
|
-
['
|
|
297
|
+
['notifyMode', 'USAGEFLEET_NOTIFY_MODE', 'alert preset: off | quiet | standard | steady | critical'],
|
|
298
|
+
['notifyThresholds', 'USAGEFLEET_NOTIFY_THRESHOLDS', '% alerts · array; env comma list (replaces the preset)'],
|
|
298
299
|
['hook', 'USAGEFLEET_HOOK', 'register the prompt guard on install (false/0 skips)'],
|
|
299
300
|
['update', 'USAGEFLEET_UPDATE', 'self-update while watching (false/0 disables)'],
|
|
300
301
|
['updateInterval', 'USAGEFLEET_UPDATE_INTERVAL', 'seconds between update checks (default 21600)'],
|
package/dist/notifier.js
CHANGED
|
@@ -1,17 +1,41 @@
|
|
|
1
1
|
import { flagOff } from './config.js';
|
|
2
2
|
import { sendNotification } from './notify.js';
|
|
3
3
|
import { readStore, storePath, updateStore } from './store.js';
|
|
4
|
-
|
|
4
|
+
/** Thresholds per mode. `critical` thins out near the wall on purpose: the last
|
|
5
|
+
* few points are where a window is actually lost, so they get a tenth-of-a-point
|
|
6
|
+
* step. Mirrored by the Devices page (apps/web/src/lib/notify-modes.ts). */
|
|
7
|
+
export const NOTIFY_MODES = {
|
|
8
|
+
critical: [80, 90, 95, 96, 97, 98, 99, 99.1, 99.2, 99.3, 99.4, 99.5, 99.6, 99.7, 99.8, 99.9, 100],
|
|
9
|
+
off: [],
|
|
10
|
+
quiet: [95, 100],
|
|
11
|
+
standard: [80, 95],
|
|
12
|
+
steady: [50, 60, 70, 80, 90, 95, 100],
|
|
13
|
+
};
|
|
14
|
+
const DEFAULT_MODE = 'standard';
|
|
15
|
+
/** Points under the mark pct must fall before the mark is lowered and re-armed. */
|
|
16
|
+
const REARM_MARGIN = 5;
|
|
17
|
+
/** Utilization % from which an alert is urgent (`critical` on Linux). */
|
|
18
|
+
const DANGER_ZONE = 95;
|
|
19
|
+
/** Case-insensitive; anything that is not a mode name is null, so a typo in one
|
|
20
|
+
* layer falls through to the next instead of silencing every alert. */
|
|
21
|
+
export function parseNotifyMode(value) {
|
|
22
|
+
const mode = typeof value === 'string' ? value.trim().toLowerCase() : '';
|
|
23
|
+
return Object.hasOwn(NOTIFY_MODES, mode) ? mode : null;
|
|
24
|
+
}
|
|
5
25
|
/** Resolve notify config, env first then the config file. Enabled by default;
|
|
6
26
|
* disable with USAGEFLEET_NOTIFY=0 (also: false/off/no) or `"notifications":
|
|
7
|
-
* false`.
|
|
8
|
-
*
|
|
9
|
-
|
|
10
|
-
|
|
27
|
+
* false`. The preset comes from USAGEFLEET_NOTIFY_MODE, the `notifyMode` file
|
|
28
|
+
* key, then `serverMode` (the one set for this device on the Devices page);
|
|
29
|
+
* an explicit list from USAGEFLEET_NOTIFY_THRESHOLDS (comma list, e.g.
|
|
30
|
+
* "50,80,95") or a `notifyThresholds` array replaces the preset's thresholds. */
|
|
31
|
+
export function loadNotifyConfig(env = process.env, file = readStore(), serverMode = null) {
|
|
32
|
+
const mode = parseNotifyMode(env.USAGEFLEET_NOTIFY_MODE) ?? parseNotifyMode(file.notifyMode) ?? serverMode ?? DEFAULT_MODE;
|
|
33
|
+
const enabled = !flagOff(env.USAGEFLEET_NOTIFY, file.notifications) && mode !== 'off';
|
|
11
34
|
const raw = env.USAGEFLEET_NOTIFY_THRESHOLDS;
|
|
12
35
|
const candidates = raw?.trim() ? raw.split(',').map(s => Number(s.trim())) : (file.notifyThresholds ?? []);
|
|
13
|
-
|
|
14
|
-
const
|
|
36
|
+
// One decimal, like the readings themselves: 99.1 must stay 99.1.
|
|
37
|
+
const parsed = candidates.map(n => Math.round(n * 10) / 10).filter(n => Number.isFinite(n) && n > 0 && n <= 100);
|
|
38
|
+
const thresholds = parsed.length > 0 ? [...new Set(parsed)].toSorted((a, b) => a - b) : NOTIFY_MODES[mode];
|
|
15
39
|
return { enabled, thresholds };
|
|
16
40
|
}
|
|
17
41
|
/**
|
|
@@ -20,8 +44,10 @@ export function loadNotifyConfig(env = process.env, file = readStore()) {
|
|
|
20
44
|
*
|
|
21
45
|
* A window rollover (resetsAt moves) resets the high-water mark first, so the
|
|
22
46
|
* first crossing in a new window always re-notifies. If utilization later drops
|
|
23
|
-
* below the mark within the SAME window (e.g. a server correction), the mark
|
|
24
|
-
* lowered so a subsequent re-cross notifies again.
|
|
47
|
+
* well below the mark within the SAME window (e.g. a server correction), the mark
|
|
48
|
+
* is lowered so a subsequent re-cross notifies again. A small dip does not move
|
|
49
|
+
* it: a group's slice is derived and wobbles between readings, and a mark that
|
|
50
|
+
* followed every wobble would re-alert each time pct straddled a threshold.
|
|
25
51
|
*/
|
|
26
52
|
export function evaluateWindow(prev, pct, resetsAt, thresholds) {
|
|
27
53
|
const rolledOver = !prev || !sameWindow(prev.resetsAt, resetsAt);
|
|
@@ -30,7 +56,7 @@ export function evaluateWindow(prev, pct, resetsAt, thresholds) {
|
|
|
30
56
|
const windowResetsAt = rolledOver ? resetsAt : prev.resetsAt;
|
|
31
57
|
if (pct == null) {
|
|
32
58
|
// No reading this cycle — keep the mark, just track the (possibly new) window.
|
|
33
|
-
return { fire: null, next: { lastBucket, resetsAt: windowResetsAt } };
|
|
59
|
+
return { fire: null, urgent: false, next: { lastBucket, resetsAt: windowResetsAt } };
|
|
34
60
|
}
|
|
35
61
|
// Highest threshold the current pct has reached (thresholds are ascending).
|
|
36
62
|
let top = 0;
|
|
@@ -40,10 +66,15 @@ export function evaluateWindow(prev, pct, resetsAt, thresholds) {
|
|
|
40
66
|
}
|
|
41
67
|
}
|
|
42
68
|
if (top > lastBucket) {
|
|
43
|
-
|
|
69
|
+
// `critical` is sticky on most Linux daemons, so only entering the danger zone and
|
|
70
|
+
// the wall get it; a dense tail (99.1, 99.2, ...) must not pile up.
|
|
71
|
+
const urgent = (lastBucket < DANGER_ZONE && top >= DANGER_ZONE) || top >= 100;
|
|
72
|
+
return { fire: top, urgent, next: { lastBucket: top, resetsAt: windowResetsAt } };
|
|
44
73
|
}
|
|
45
|
-
// top <= lastBucket:
|
|
46
|
-
|
|
74
|
+
// top <= lastBucket: nothing fires. The mark holds through a wobble and follows
|
|
75
|
+
// pct down only on a real drop.
|
|
76
|
+
const mark = pct < lastBucket - REARM_MARGIN ? top : lastBucket;
|
|
77
|
+
return { fire: null, urgent: false, next: { lastBucket: mark, resetsAt: windowResetsAt } };
|
|
47
78
|
}
|
|
48
79
|
// oauth/usage jitters resets_at by milliseconds per request; a real rollover moves it by hours.
|
|
49
80
|
function sameWindow(a, b) {
|
|
@@ -71,9 +102,6 @@ function resetSuffix(resetsAt) {
|
|
|
71
102
|
}
|
|
72
103
|
return ` · resets in ${Math.round(h / 24)}d`;
|
|
73
104
|
}
|
|
74
|
-
function urgencyFor(bucket) {
|
|
75
|
-
return bucket >= 95 ? 'critical' : 'normal';
|
|
76
|
-
}
|
|
77
105
|
/**
|
|
78
106
|
* Notify on freshly-crossed 5h/weekly thresholds, deduped across runs via the
|
|
79
107
|
* store's `notify` section. Best-effort and self-contained: it owns its state
|
|
@@ -96,11 +124,11 @@ export function maybeNotify(report, cfg = loadNotifyConfig(), log = () => {
|
|
|
96
124
|
const fivePct = fmt(report.fiveHourPct ?? 0);
|
|
97
125
|
const sevenPct = fmt(report.sevenDayPct ?? 0);
|
|
98
126
|
if (five.fire != null) {
|
|
99
|
-
sendNotification('Claude usage · 5-hour limit', `${fivePct}% of your 5-hour limit used${resetSuffix(report.fiveHourResetsAt)}.`, { urgency:
|
|
127
|
+
sendNotification('Claude usage · 5-hour limit', `${fivePct}% of your 5-hour limit used${resetSuffix(report.fiveHourResetsAt)}.`, { urgency: five.urgent ? 'critical' : 'normal' });
|
|
100
128
|
log('ok', `notified · 5h at ${fivePct}% · crossed ${five.fire}%`);
|
|
101
129
|
}
|
|
102
130
|
if (seven.fire != null) {
|
|
103
|
-
sendNotification('Claude usage · weekly limit', `${sevenPct}% of your weekly limit used${resetSuffix(report.sevenDayResetsAt)}.`, { urgency:
|
|
131
|
+
sendNotification('Claude usage · weekly limit', `${sevenPct}% of your weekly limit used${resetSuffix(report.sevenDayResetsAt)}.`, { urgency: seven.urgent ? 'critical' : 'normal' });
|
|
104
132
|
log('ok', `notified · weekly at ${sevenPct}% · crossed ${seven.fire}%`);
|
|
105
133
|
}
|
|
106
134
|
updateStore(path, store => {
|
package/dist/release.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Generated by .github/workflows/release.yml.
|
|
2
|
-
export const RELEASE_VERSION = "1.2.
|
|
2
|
+
export const RELEASE_VERSION = "1.2.173";
|
package/dist/store.js
CHANGED
|
@@ -61,6 +61,7 @@ function normalize(raw) {
|
|
|
61
61
|
fiveHour: { ...freshWindow(), ...raw.notify?.fiveHour },
|
|
62
62
|
sevenDay: { ...freshWindow(), ...raw.notify?.sevenDay },
|
|
63
63
|
},
|
|
64
|
+
notifyMode: raw.notifyMode,
|
|
64
65
|
notifyThresholds: raw.notifyThresholds,
|
|
65
66
|
piDir: raw.piDir,
|
|
66
67
|
pjDir: raw.pjDir,
|
package/dist/uploader.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ENDPOINT, tokenFor } from './config.js';
|
|
2
|
+
import { parseNotifyMode } from './notifier.js';
|
|
2
3
|
const MAX_ATTEMPTS = 6;
|
|
3
4
|
const REQUEST_TIMEOUT_MS = 15_000;
|
|
4
5
|
function sleep(ms) {
|
|
@@ -115,7 +116,12 @@ export async function fetchGroupLimits(cfg, accountExtId) {
|
|
|
115
116
|
if (typeof body?.sessionPct !== 'number' || typeof body.weeklyPct !== 'number') {
|
|
116
117
|
return null;
|
|
117
118
|
}
|
|
118
|
-
return {
|
|
119
|
+
return {
|
|
120
|
+
group: body.group ?? null,
|
|
121
|
+
notifyMode: parseNotifyMode(body.notifyMode),
|
|
122
|
+
sessionPct: body.sessionPct,
|
|
123
|
+
weeklyPct: body.weeklyPct,
|
|
124
|
+
};
|
|
119
125
|
}
|
|
120
126
|
catch {
|
|
121
127
|
return null;
|