@usagefleet/cli 1.2.97 → 1.2.99
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 +19 -18
- package/dist/config.js +22 -1
- package/dist/hook.js +5 -3
- package/dist/index.js +39 -27
- package/dist/notifier.js +10 -16
- package/dist/pi-hook.js +5 -2
- package/dist/release.js +1 -1
- package/dist/store.js +12 -1
- package/dist/update.js +3 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -126,24 +126,25 @@ the CLI persists: your settings plus two machine-managed sections, `state` (tail
|
|
|
126
126
|
offsets) and `notify` (which thresholds already fired). Delete it to start
|
|
127
127
|
clean. Re-running `login` merges, so rotating a token doesn't reset offsets.
|
|
128
128
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
| `
|
|
135
|
-
| `
|
|
136
|
-
| `
|
|
137
|
-
| `
|
|
138
|
-
| `
|
|
139
|
-
| `
|
|
140
|
-
| `
|
|
141
|
-
| `
|
|
142
|
-
| `
|
|
143
|
-
| `USAGEFLEET_UPDATE` | `0` turns the self-update check off |
|
|
144
|
-
| `USAGEFLEET_UPDATE_INTERVAL` | seconds between update checks (default `21600` = 6h) |
|
|
145
|
-
| `USAGEFLEET_HOOK` | `0` keeps the prompt-blocking guard out of Claude Code and pi |
|
|
146
|
-
| `
|
|
129
|
+
Every knob is a top-level key in that file, and the matching env var overrides
|
|
130
|
+
it (`usagefleet config` prints this same table):
|
|
131
|
+
|
|
132
|
+
| File key | Env override | Meaning |
|
|
133
|
+
|----------|--------------|---------|
|
|
134
|
+
| `token` | `USAGEFLEET_TOKEN` | device token |
|
|
135
|
+
| `projectsDir` | `USAGEFLEET_PROJECTS` | override `~/.claude/projects` |
|
|
136
|
+
| `desktopDir` | `USAGEFLEET_DESKTOP` | override the Claude Desktop sessions dir; `off` to skip it |
|
|
137
|
+
| `piDir` | `USAGEFLEET_PI` | pi sessions dirs — string or array in the file, comma-separated in env; `off` to skip |
|
|
138
|
+
| `interval` | `USAGEFLEET_INTERVAL` | watch poll seconds (default 15) |
|
|
139
|
+
| `limitsInterval` | `USAGEFLEET_LIMITS_INTERVAL` | seconds between limit reports (default 60; 300 on API keys, where each report costs a 1-token ping) |
|
|
140
|
+
| `notifications` | `USAGEFLEET_NOTIFY` | desktop notifications, on by default (`false`/`0` disables) |
|
|
141
|
+
| `notifyThresholds` | `USAGEFLEET_NOTIFY_THRESHOLDS` | utilization % that trigger an alert — array in the file, comma list in env (default `80,95`) |
|
|
142
|
+
| `batch` | `USAGEFLEET_BATCH` | records per upload (default 100, server caps at 1000) |
|
|
143
|
+
| `update` | `USAGEFLEET_UPDATE` | `false`/`0` turns the self-update check off |
|
|
144
|
+
| `updateInterval` | `USAGEFLEET_UPDATE_INTERVAL` | seconds between update checks (default `21600` = 6h) |
|
|
145
|
+
| `hook` | `USAGEFLEET_HOOK` | `false`/`0` keeps the prompt-blocking guard out of Claude Code and pi |
|
|
146
|
+
| — | `USAGEFLEET_CONFIG` | override the config file path (env only — it locates the file) |
|
|
147
|
+
| — | `CLAUDE_CONFIG_DIR` | Claude Code's own knob: which login to watch (default `~/.claude`) |
|
|
147
148
|
|
|
148
149
|
When run as a service, `login` bakes every `USAGEFLEET_*` value currently set
|
|
149
150
|
(plus `ANTHROPIC_API_KEY` and `CLAUDE_CONFIG_DIR`) into the launchd/systemd
|
package/dist/config.js
CHANGED
|
@@ -18,7 +18,7 @@ export function loadConfig() {
|
|
|
18
18
|
// Guard batch size: "0" (infinite loop), NaN (silent drop), fractional → 100.
|
|
19
19
|
// Clamped to the server's own 1000-record cap, since a larger batch is
|
|
20
20
|
// rejected as malformed and would cost the whole chunk a bisect to discover.
|
|
21
|
-
const parsedBatch = Math.floor(
|
|
21
|
+
const parsedBatch = Math.floor(positiveNumber(process.env.USAGEFLEET_BATCH, file.batch) ?? 100);
|
|
22
22
|
const batchSize = Number.isFinite(parsedBatch) && parsedBatch > 0 ? Math.min(parsedBatch, MAX_BATCH) : 100;
|
|
23
23
|
return {
|
|
24
24
|
batchSize,
|
|
@@ -29,6 +29,27 @@ export function loadConfig() {
|
|
|
29
29
|
token,
|
|
30
30
|
};
|
|
31
31
|
}
|
|
32
|
+
/** env → file → nothing, for the numeric knobs (intervals, batch): an env
|
|
33
|
+
* value wins when present (empty string counts as unset, same `||` rule as
|
|
34
|
+
* loadConfig), and a non-positive or non-numeric candidate is skipped rather
|
|
35
|
+
* than trusted. */
|
|
36
|
+
export function positiveNumber(env, fromFile) {
|
|
37
|
+
for (const candidate of [env ? Number(env) : null, fromFile]) {
|
|
38
|
+
if (typeof candidate === 'number' && Number.isFinite(candidate) && candidate > 0) {
|
|
39
|
+
return candidate;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
/** The off-switches (hook, update, notifications): an env value wins when
|
|
45
|
+
* present — 0/false/off/no disable, anything else enables — otherwise `false`
|
|
46
|
+
* under the config-file key disables. */
|
|
47
|
+
export function flagOff(env, fromFile) {
|
|
48
|
+
if (env) {
|
|
49
|
+
return /^(0|false|off|no)$/i.test(env.trim());
|
|
50
|
+
}
|
|
51
|
+
return fromFile === false;
|
|
52
|
+
}
|
|
32
53
|
/** pi scan roots: env "off"/"0" disables, else a comma-separated env list, else
|
|
33
54
|
* the config file's string-or-array, else every auto-detected default. */
|
|
34
55
|
export function resolvePiDirs(env, fromFile) {
|
package/dist/hook.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { mkdirSync, readFileSync } from 'node:fs';
|
|
2
2
|
import { dirname } from 'node:path';
|
|
3
3
|
import { writeFileAtomic } from './atomic-write.js';
|
|
4
|
+
import { flagOff } from './config.js';
|
|
4
5
|
import { claudeSettingsPath } from './paths.js';
|
|
6
|
+
import { readStore } from './store.js';
|
|
5
7
|
import { step, tilde, warn } from './ui.js';
|
|
6
8
|
/** Outer bound on the hook, in seconds. runGuard's own fetch gives up after 5s
|
|
7
9
|
* and fails open; this only matters if the process itself wedges. */
|
|
@@ -102,11 +104,11 @@ function editSettings(transform, onWrite) {
|
|
|
102
104
|
/**
|
|
103
105
|
* Register `usagefleet guard` as a Claude Code UserPromptSubmit hook, so a
|
|
104
106
|
* group with blocking enabled actually refuses prompts. Called by
|
|
105
|
-
* `usagefleet login`;
|
|
106
|
-
* untouched.
|
|
107
|
+
* `usagefleet login`; USAGEFLEET_HOOK=0 or `"hook": false` in the config file
|
|
108
|
+
* keeps settings.json untouched.
|
|
107
109
|
*/
|
|
108
110
|
export function installPromptHook(program) {
|
|
109
|
-
if (process.env.USAGEFLEET_HOOK
|
|
111
|
+
if (flagOff(process.env.USAGEFLEET_HOOK, readStore().hook)) {
|
|
110
112
|
return;
|
|
111
113
|
}
|
|
112
114
|
const command = guardCommand(program, process.env.USAGEFLEET_CONFIG);
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { detectClaudeCreds } from './claude-creds.js';
|
|
3
3
|
import { reportLimitsOnce, runOnce } from './collector.js';
|
|
4
4
|
import { commands, completionScript, installCompletions, removeCompletions, shells, suggest } from './completion.js';
|
|
5
|
-
import { ENDPOINT, loadConfig } from './config.js';
|
|
5
|
+
import { ENDPOINT, loadConfig, positiveNumber } from './config.js';
|
|
6
6
|
import { runGuard } from './guard.js';
|
|
7
7
|
import { loadNotifyConfig } from './notifier.js';
|
|
8
8
|
import { sendNotification } from './notify.js';
|
|
@@ -68,22 +68,25 @@ async function cmdLimits() {
|
|
|
68
68
|
}
|
|
69
69
|
async function cmdWatch() {
|
|
70
70
|
const cfg = loadConfig();
|
|
71
|
-
const
|
|
71
|
+
const settings = readStore(cfg.storePath);
|
|
72
|
+
const raw = flag('interval') == null
|
|
73
|
+
? (positiveNumber(process.env.USAGEFLEET_INTERVAL, settings.interval) ?? 15)
|
|
74
|
+
: Number(flag('interval'));
|
|
72
75
|
const interval = Math.max(1, Number.isFinite(raw) && raw > 0 ? raw : 15) * 1000;
|
|
73
76
|
// Limits reporting is decoupled from the much faster usage poll. Default
|
|
74
77
|
// interval depends on how the reading is fetched: subscription logins use the
|
|
75
78
|
// free oauth/usage endpoint (60s keeps the dashboard split fresh at zero token
|
|
76
79
|
// cost), API keys pay a 1-token Messages ping per reading (300s). An explicit
|
|
77
|
-
// USAGEFLEET_LIMITS_INTERVAL overrides both.
|
|
78
|
-
const rawLimits =
|
|
79
|
-
const explicitLimits =
|
|
80
|
+
// USAGEFLEET_LIMITS_INTERVAL or `limitsInterval` file key overrides both.
|
|
81
|
+
const rawLimits = positiveNumber(process.env.USAGEFLEET_LIMITS_INTERVAL, settings.limitsInterval);
|
|
82
|
+
const explicitLimits = rawLimits ? rawLimits * 1000 : null;
|
|
80
83
|
let limitsInterval = explicitLimits ?? 60_000;
|
|
81
84
|
let lastLimitsAt = 0;
|
|
82
85
|
// Self-update: once at startup, then every USAGEFLEET_UPDATE_INTERVAL seconds
|
|
83
86
|
// (default 6h — a release lands on a device the same day, not the next).
|
|
84
|
-
// USAGEFLEET_UPDATE=0 opts out.
|
|
85
|
-
const rawUpdate =
|
|
86
|
-
const updateInterval = Math.max(60,
|
|
87
|
+
// USAGEFLEET_UPDATE=0 or `"update": false` opts out.
|
|
88
|
+
const rawUpdate = positiveNumber(process.env.USAGEFLEET_UPDATE_INTERVAL, settings.updateInterval) ?? 6 * 60 * 60;
|
|
89
|
+
const updateInterval = Math.max(60, rawUpdate) * 1000;
|
|
87
90
|
let lastUpdateAt = 0;
|
|
88
91
|
const watching = [cfg.projectsDir, cfg.desktopDir, ...cfg.piDirs].filter((d) => !!d);
|
|
89
92
|
console.log(header(`watching every ${interval / 1000}s`));
|
|
@@ -154,7 +157,7 @@ async function cmdWatch() {
|
|
|
154
157
|
function cmdNotifyTest() {
|
|
155
158
|
const cfg = loadNotifyConfig();
|
|
156
159
|
if (!cfg.enabled) {
|
|
157
|
-
console.log(warn('notify', 'disabled · unset USAGEFLEET_NOTIFY=0 to enable'));
|
|
160
|
+
console.log(warn('notify', 'disabled · unset USAGEFLEET_NOTIFY=0 / `"notifications": false` to enable'));
|
|
158
161
|
return;
|
|
159
162
|
}
|
|
160
163
|
sendNotification('usagefleet', 'Test notification — desktop alerts are working.', {
|
|
@@ -245,31 +248,40 @@ function print(rows) {
|
|
|
245
248
|
console.log(` ${name.padEnd(width)} ${dim(meaning)}`);
|
|
246
249
|
}
|
|
247
250
|
}
|
|
248
|
-
/** Where settings live
|
|
249
|
-
*
|
|
251
|
+
/** Where settings live: every knob is a key in the config file, and the
|
|
252
|
+
* matching env var overrides it. Reads nothing: it must work before a token
|
|
253
|
+
* exists, when the config is what you're fixing. */
|
|
250
254
|
function cmdConfig() {
|
|
251
|
-
const
|
|
252
|
-
['USAGEFLEET_TOKEN', 'device token from the Devices page'],
|
|
253
|
-
['USAGEFLEET_PROJECTS', 'override ~/.claude/projects'],
|
|
254
|
-
['USAGEFLEET_DESKTOP', 'override the Claude Desktop dir ("off" disables)'],
|
|
255
|
-
['USAGEFLEET_PI', '
|
|
256
|
-
['USAGEFLEET_INTERVAL', 'watch interval seconds (default 15)'],
|
|
257
|
-
[
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
['
|
|
263
|
-
['
|
|
264
|
-
['
|
|
255
|
+
const knobs = [
|
|
256
|
+
['token', 'USAGEFLEET_TOKEN', 'device token from the Devices page'],
|
|
257
|
+
['projectsDir', 'USAGEFLEET_PROJECTS', 'override ~/.claude/projects'],
|
|
258
|
+
['desktopDir', 'USAGEFLEET_DESKTOP', 'override the Claude Desktop dir ("off" disables)'],
|
|
259
|
+
['piDir', 'USAGEFLEET_PI', 'pi session dirs · string or array; env comma-separated'],
|
|
260
|
+
['interval', 'USAGEFLEET_INTERVAL', 'watch interval seconds (default 15)'],
|
|
261
|
+
[
|
|
262
|
+
'limitsInterval',
|
|
263
|
+
'USAGEFLEET_LIMITS_INTERVAL',
|
|
264
|
+
'seconds between limits reports (default 60; 300 on API keys)',
|
|
265
|
+
],
|
|
266
|
+
['batch', 'USAGEFLEET_BATCH', 'records per upload (default 100, max 1000)'],
|
|
267
|
+
['notifications', 'USAGEFLEET_NOTIFY', 'desktop notifications (false/0 disables)'],
|
|
268
|
+
['notifyThresholds', 'USAGEFLEET_NOTIFY_THRESHOLDS', '% alerts · array; env comma list (default 80,95)'],
|
|
269
|
+
['hook', 'USAGEFLEET_HOOK', 'register the prompt guard on install (false/0 skips)'],
|
|
270
|
+
['update', 'USAGEFLEET_UPDATE', 'self-update while watching (false/0 disables)'],
|
|
271
|
+
['updateInterval', 'USAGEFLEET_UPDATE_INTERVAL', 'seconds between update checks (default 21600)'],
|
|
272
|
+
['—', 'USAGEFLEET_CONFIG', 'relocate the config file (env only — it locates the file)'],
|
|
265
273
|
];
|
|
266
274
|
console.log(header());
|
|
267
275
|
console.log(hint('settings, tail offsets and notification marks'));
|
|
268
276
|
console.log('');
|
|
269
277
|
console.log(row('file', tilde(storePath())));
|
|
270
278
|
console.log('');
|
|
271
|
-
console.log(hint('
|
|
272
|
-
|
|
279
|
+
console.log(hint('file key · env override wins'));
|
|
280
|
+
const keyWidth = Math.max(...knobs.map(([key]) => key.length));
|
|
281
|
+
const envWidth = Math.max(...knobs.map(([, env]) => env.length));
|
|
282
|
+
for (const [key, env, meaning] of knobs) {
|
|
283
|
+
console.log(` ${key.padEnd(keyWidth)} ${env.padEnd(envWidth)} ${dim(meaning)}`);
|
|
284
|
+
}
|
|
273
285
|
console.log('');
|
|
274
286
|
console.log(hint('`usagefleet status` shows the resolved values'));
|
|
275
287
|
}
|
package/dist/notifier.js
CHANGED
|
@@ -1,23 +1,17 @@
|
|
|
1
|
+
import { flagOff } from './config.js';
|
|
1
2
|
import { sendNotification } from './notify.js';
|
|
2
3
|
import { readStore, storePath, updateStore } from './store.js';
|
|
3
4
|
const DEFAULT_THRESHOLDS = [80, 95];
|
|
4
|
-
/** Resolve notify config
|
|
5
|
-
* USAGEFLEET_NOTIFY=0 (also: false/off/no)
|
|
6
|
-
* USAGEFLEET_NOTIFY_THRESHOLDS as a comma list
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const enabled =
|
|
10
|
-
let thresholds = DEFAULT_THRESHOLDS;
|
|
5
|
+
/** Resolve notify config, env first then the config file. Enabled by default;
|
|
6
|
+
* disable with USAGEFLEET_NOTIFY=0 (also: false/off/no) or `"notifications":
|
|
7
|
+
* false`. Thresholds from USAGEFLEET_NOTIFY_THRESHOLDS as a comma list
|
|
8
|
+
* (e.g. "50,80,95") or a `notifyThresholds` array. */
|
|
9
|
+
export function loadNotifyConfig(env = process.env, file = readStore()) {
|
|
10
|
+
const enabled = !flagOff(env.USAGEFLEET_NOTIFY, file.notifications);
|
|
11
11
|
const raw = env.USAGEFLEET_NOTIFY_THRESHOLDS;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
.map(s => Math.round(Number(s.trim())))
|
|
16
|
-
.filter(n => Number.isFinite(n) && n > 0 && n <= 100);
|
|
17
|
-
if (parsed.length > 0) {
|
|
18
|
-
thresholds = [...new Set(parsed)].toSorted((a, b) => a - b);
|
|
19
|
-
}
|
|
20
|
-
}
|
|
12
|
+
const candidates = raw?.trim() ? raw.split(',').map(s => Number(s.trim())) : (file.notifyThresholds ?? []);
|
|
13
|
+
const parsed = candidates.map(Math.round).filter(n => Number.isFinite(n) && n > 0 && n <= 100);
|
|
14
|
+
const thresholds = parsed.length > 0 ? [...new Set(parsed)].toSorted((a, b) => a - b) : DEFAULT_THRESHOLDS;
|
|
21
15
|
return { enabled, thresholds };
|
|
22
16
|
}
|
|
23
17
|
/**
|
package/dist/pi-hook.js
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, readFileSync, rmSync } from 'node:fs';
|
|
2
2
|
import { dirname, join } from 'node:path';
|
|
3
3
|
import { writeFileAtomic } from './atomic-write.js';
|
|
4
|
+
import { flagOff } from './config.js';
|
|
4
5
|
import { piAgentDir } from './paths.js';
|
|
6
|
+
import { readStore } from './store.js';
|
|
5
7
|
import { step, tilde } from './ui.js';
|
|
6
8
|
/** pi has no settings-file hooks; its extension point is a TypeScript file
|
|
7
9
|
* auto-discovered from `<agent-dir>/extensions/*.ts`. So the pi guard is a
|
|
8
10
|
* generated file rather than a settings entry, with the same lifecycle as the
|
|
9
11
|
* Claude hook: written by `login` (which self-update re-runs, refreshing the
|
|
10
|
-
* baked binary path), removed by `uninstall`, skipped by USAGEFLEET_HOOK=0
|
|
12
|
+
* baked binary path), removed by `uninstall`, skipped by USAGEFLEET_HOOK=0 or
|
|
13
|
+
* `"hook": false` in the config file. */
|
|
11
14
|
const FILE = 'usagefleet-guard.ts';
|
|
12
15
|
export function piGuardPath(agentDir = piAgentDir()) {
|
|
13
16
|
return join(agentDir, 'extensions', FILE);
|
|
@@ -70,7 +73,7 @@ export default function (pi: { on(event: 'input', handler: InputHandler): void }
|
|
|
70
73
|
/** Skips machines without pi (agent dir absent) rather than conjuring a `.pi`
|
|
71
74
|
* tree that pi itself never made. */
|
|
72
75
|
export function installPiGuard(program, agentDir = piAgentDir()) {
|
|
73
|
-
if (process.env.USAGEFLEET_HOOK
|
|
76
|
+
if (flagOff(process.env.USAGEFLEET_HOOK, readStore().hook) || !existsSync(agentDir)) {
|
|
74
77
|
return;
|
|
75
78
|
}
|
|
76
79
|
const path = piGuardPath(agentDir);
|
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.99";
|
package/dist/store.js
CHANGED
|
@@ -41,15 +41,24 @@ function readJson(path) {
|
|
|
41
41
|
export function freshWindow() {
|
|
42
42
|
return { lastBucket: 0, resetsAt: null };
|
|
43
43
|
}
|
|
44
|
-
/** Fill in every field so callers get a total value, whatever the file held.
|
|
44
|
+
/** Fill in every field so callers get a total value, whatever the file held.
|
|
45
|
+
* Listing fields explicitly (rather than spreading `raw`) is what drops junk
|
|
46
|
+
* keys — so every new settings key must be carried through here or the next
|
|
47
|
+
* `updateStore` silently erases it. */
|
|
45
48
|
function normalize(raw) {
|
|
46
49
|
return {
|
|
50
|
+
batch: raw.batch,
|
|
47
51
|
desktopDir: raw.desktopDir,
|
|
52
|
+
hook: raw.hook,
|
|
53
|
+
interval: raw.interval,
|
|
48
54
|
limits: raw.limits,
|
|
55
|
+
limitsInterval: raw.limitsInterval,
|
|
56
|
+
notifications: raw.notifications,
|
|
49
57
|
notify: {
|
|
50
58
|
fiveHour: { ...freshWindow(), ...raw.notify?.fiveHour },
|
|
51
59
|
sevenDay: { ...freshWindow(), ...raw.notify?.sevenDay },
|
|
52
60
|
},
|
|
61
|
+
notifyThresholds: raw.notifyThresholds,
|
|
53
62
|
piDir: raw.piDir,
|
|
54
63
|
projectsDir: raw.projectsDir,
|
|
55
64
|
state: {
|
|
@@ -58,6 +67,8 @@ function normalize(raw) {
|
|
|
58
67
|
updatedAt: raw.state?.updatedAt ?? new Date().toISOString(),
|
|
59
68
|
},
|
|
60
69
|
token: raw.token,
|
|
70
|
+
update: raw.update,
|
|
71
|
+
updateInterval: raw.updateInterval,
|
|
61
72
|
version: 1,
|
|
62
73
|
};
|
|
63
74
|
}
|
package/dist/update.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
2
|
import { existsSync, readFileSync, realpathSync } from 'node:fs';
|
|
3
3
|
import { basename, delimiter, dirname, join } from 'node:path';
|
|
4
|
+
import { flagOff } from './config.js';
|
|
4
5
|
import { RELEASE_VERSION } from './release.js';
|
|
6
|
+
import { readStore } from './store.js';
|
|
5
7
|
import { tilde } from './ui.js';
|
|
6
8
|
/** The published package: one artifact for every OS, installed with
|
|
7
9
|
* `npm i -g @usagefleet/cli`. */
|
|
@@ -157,7 +159,7 @@ export async function checkForUpdate(log, force = false) {
|
|
|
157
159
|
if (!self) {
|
|
158
160
|
return null;
|
|
159
161
|
}
|
|
160
|
-
if (!force && process.env.USAGEFLEET_UPDATE
|
|
162
|
+
if (!force && flagOff(process.env.USAGEFLEET_UPDATE, readStore().update)) {
|
|
161
163
|
return null;
|
|
162
164
|
}
|
|
163
165
|
// A rejected fetch (offline, DNS, timeout) must stay inside this function:
|