@usagefleet/cli 1.2.70 → 1.2.72
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 +126 -231
- package/dist/completion.js +56 -0
- package/dist/index.js +15 -14
- package/dist/release.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,200 +1,93 @@
|
|
|
1
1
|
# @usagefleet/cli
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
UsageFleet server. Read-only on the log files. Zero runtime dependencies
|
|
8
|
-
(Node ≥ 20).
|
|
3
|
+
The UsageFleet collector. It tails the local logs of your Claude agents and
|
|
4
|
+
reports usage to [usagefleet.com](https://usagefleet.com), where it's split
|
|
5
|
+
across your device groups. Read-only on the log files, zero runtime
|
|
6
|
+
dependencies, Node ≥ 20.
|
|
9
7
|
|
|
10
8
|
## Install
|
|
11
9
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
`https://usagefleet.com`):
|
|
10
|
+
Get a device **token** from the [Devices](https://usagefleet.com/devices) page,
|
|
11
|
+
then, on that machine:
|
|
15
12
|
|
|
16
13
|
```bash
|
|
17
14
|
npm i -g @usagefleet/cli
|
|
18
|
-
usagefleet install --token uf_xxx
|
|
15
|
+
usagefleet install --token uf_xxx
|
|
19
16
|
```
|
|
20
17
|
|
|
21
|
-
|
|
22
|
-
no
|
|
18
|
+
Same two commands on macOS, Linux and Windows (in PowerShell chain them with
|
|
19
|
+
`;` — 5.1 has no `&&`). `install` sets the collector to start at login and
|
|
20
|
+
writes `~/.config/usagefleet/config.json` (mode `600`). The dashboard fills in
|
|
21
|
+
within a minute.
|
|
23
22
|
|
|
24
|
-
|
|
25
|
-
(Windows). If `npm i -g` fails with EACCES your global prefix is root-owned:
|
|
26
|
-
either use a Node version manager (nvm, fnm, volta) or
|
|
27
|
-
`npm config set prefix ~/.local` and put `~/.local/bin` on your PATH. Installing
|
|
28
|
-
the collector under `sudo` would run it as the wrong user.
|
|
23
|
+
Running your own server? Add `--endpoint https://track.example.com`.
|
|
29
24
|
|
|
30
|
-
|
|
31
|
-
|
|
25
|
+
If `npm i -g` fails with EACCES your global prefix is root-owned: use a Node
|
|
26
|
+
version manager (nvm, fnm, volta) or `npm config set prefix ~/.local` with
|
|
27
|
+
`~/.local/bin` on your PATH. Don't install under `sudo` — the collector would
|
|
28
|
+
run as the wrong user.
|
|
32
29
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
```bash
|
|
36
|
-
cd apps/cli
|
|
37
|
-
bun install
|
|
38
|
-
bun run src/index.ts status # or: npm run build && node dist/index.js status
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
## Configure
|
|
42
|
-
|
|
43
|
-
Get a device **token** from the server's Devices page, then either:
|
|
44
|
-
|
|
45
|
-
```bash
|
|
46
|
-
usagefleet install --endpoint https://track.example.com --token uf_xxx
|
|
47
|
-
# writes ~/.config/usagefleet/config.json
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
That one file (mode `600`) holds everything the CLI persists — your settings
|
|
51
|
-
plus the two machine-managed sections, `state` (per-log tail offsets) and
|
|
52
|
-
`notify` (which alert thresholds already fired). Delete it to start clean.
|
|
53
|
-
`XDG_CONFIG_HOME` is honoured. Installs predating the consolidation are folded
|
|
54
|
-
in automatically from `~/.usagefleet.json`, `~/.usagefleet-state.json` and
|
|
55
|
-
`~/.usagefleet-notify.json` on first write; those files are then unused and
|
|
56
|
-
safe to delete.
|
|
57
|
-
|
|
58
|
-
Or set env vars (they override the config file):
|
|
59
|
-
|
|
60
|
-
| Variable | Meaning |
|
|
61
|
-
|----------|---------|
|
|
62
|
-
| `USAGEFLEET_ENDPOINT` | server base URL. Must be `https://` (loopback may be `http://`): it carries the device token on every request |
|
|
63
|
-
| `USAGEFLEET_TOKEN` | device token |
|
|
64
|
-
| `USAGEFLEET_PROJECTS` | override `~/.claude/projects` (Claude Code logs) |
|
|
65
|
-
| `USAGEFLEET_DESKTOP` | override the Claude Desktop agent-mode sessions dir (auto-detected per-OS); set `off`/`0` to skip desktop collection |
|
|
66
|
-
| `USAGEFLEET_PI` | override the pi agent sessions dirs, comma-separated (default `~/.pi/agent/sessions` plus whatever `PI_CODING_AGENT_DIR`/`PI_CODING_AGENT_SESSION_DIR` point at — a service inherits neither, so set this if you relocated pi's agent dir); set `off`/`0` to skip pi collection |
|
|
67
|
-
| `USAGEFLEET_INTERVAL` | watch poll seconds (default 15) |
|
|
68
|
-
| `USAGEFLEET_LIMITS_INTERVAL` | how often to ping for real 5h/weekly limits, seconds (default 300; decoupled from the faster usage poll so the 1-token ping doesn't run every cycle) |
|
|
69
|
-
| `USAGEFLEET_NOTIFY` | desktop notifications on/off (default **on**; set `0`/`false`/`off` to disable) |
|
|
70
|
-
| `USAGEFLEET_NOTIFY_THRESHOLDS` | comma list of utilization % that trigger an alert (default `80,95`) |
|
|
71
|
-
| `USAGEFLEET_BATCH` | records per upload request (default 100, capped at the server's limit of 1000) |
|
|
72
|
-
| `USAGEFLEET_CONFIG` | override the whole config file path (default `~/.config/usagefleet/config.json`) |
|
|
73
|
-
| `USAGEFLEET_UPDATE` | set `0` to turn the self-update check off |
|
|
74
|
-
| `USAGEFLEET_UPDATE_INTERVAL` | seconds between self-update checks (default `21600` = 6h, i.e. 4×/day) |
|
|
75
|
-
| `USAGEFLEET_HOOK` | set `0` to keep the prompt-blocking hook out of `~/.claude/settings.json` |
|
|
76
|
-
|
|
77
|
-
### Setting the token per shell
|
|
78
|
-
|
|
79
|
-
**bash / zsh** (macOS, Linux) — one-off for the current session:
|
|
80
|
-
|
|
81
|
-
```bash
|
|
82
|
-
export USAGEFLEET_ENDPOINT="https://track.example.com"
|
|
83
|
-
export USAGEFLEET_TOKEN="uf_xxx"
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
Persist it by appending those lines to `~/.bashrc` / `~/.zshrc`, then
|
|
87
|
-
`source ~/.zshrc`. Or set it inline for a single command:
|
|
88
|
-
|
|
89
|
-
```bash
|
|
90
|
-
USAGEFLEET_ENDPOINT=https://track.example.com USAGEFLEET_TOKEN=uf_xxx usagefleet run
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
**fish**:
|
|
94
|
-
|
|
95
|
-
```fish
|
|
96
|
-
set -x USAGEFLEET_ENDPOINT "https://track.example.com"
|
|
97
|
-
set -x USAGEFLEET_TOKEN "uf_xxx"
|
|
98
|
-
# persist (writes to universal vars, survives restarts):
|
|
99
|
-
set -Ux USAGEFLEET_TOKEN "uf_xxx"
|
|
100
|
-
set -Ux USAGEFLEET_ENDPOINT "https://track.example.com"
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
**PowerShell** (Windows) — current session:
|
|
104
|
-
|
|
105
|
-
```powershell
|
|
106
|
-
$env:USAGEFLEET_ENDPOINT = "https://track.example.com"
|
|
107
|
-
$env:USAGEFLEET_TOKEN = "uf_xxx"
|
|
108
|
-
# persist for your user (new shells only):
|
|
109
|
-
setx USAGEFLEET_TOKEN "uf_xxx"
|
|
110
|
-
setx USAGEFLEET_ENDPOINT "https://track.example.com"
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
**cmd.exe** (Windows):
|
|
114
|
-
|
|
115
|
-
```cmd
|
|
116
|
-
set USAGEFLEET_ENDPOINT=https://track.example.com
|
|
117
|
-
set USAGEFLEET_TOKEN=uf_xxx
|
|
118
|
-
:: persist: setx USAGEFLEET_TOKEN "uf_xxx"
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
> Prefer not to put a long-lived token in shell history/rc files? Use
|
|
122
|
-
> `usagefleet install --endpoint <url> --token <t>` instead — it writes
|
|
123
|
-
> `~/.config/usagefleet/config.json` (mode `600`), which the collector reads
|
|
124
|
-
> automatically. The write merges, so re-running it rotates the token without
|
|
125
|
-
> resetting your tail offsets.
|
|
126
|
-
> When run as a service, `usagefleet install` bakes every `USAGEFLEET_*` value
|
|
127
|
-
> that is currently set (plus `ANTHROPIC_API_KEY`) into the launchd/systemd unit.
|
|
128
|
-
> The unit is written mode `600`, since it holds those secrets.
|
|
129
|
-
|
|
130
|
-
## Run
|
|
30
|
+
## Commands
|
|
131
31
|
|
|
132
32
|
```bash
|
|
33
|
+
usagefleet status # service health, last limits reading, resolved config
|
|
133
34
|
usagefleet run # one scan: upload usage + report limits
|
|
134
|
-
usagefleet watch # poll continuously
|
|
35
|
+
usagefleet watch # poll continuously (what the service runs)
|
|
135
36
|
usagefleet limits # report ONLY your real 5h/weekly limit usage
|
|
136
37
|
usagefleet guard # exit 2 if this device's group is over a blocking limit
|
|
137
|
-
usagefleet
|
|
138
|
-
usagefleet
|
|
38
|
+
usagefleet notify-test # fire a sample desktop notification
|
|
39
|
+
usagefleet update # upgrade now (it also self-updates every 6h)
|
|
139
40
|
usagefleet config # config file location + every env override
|
|
140
|
-
usagefleet
|
|
41
|
+
usagefleet install # (re)install the background service, idempotent
|
|
42
|
+
usagefleet uninstall # remove it
|
|
43
|
+
usagefleet completion zsh # print a shell completion script (zsh, fish)
|
|
141
44
|
```
|
|
142
45
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
`watch` asks the npm registry for the published version at startup and then
|
|
146
|
-
every 6 hours (`USAGEFLEET_UPDATE_INTERVAL`, in seconds); when it differs from
|
|
147
|
-
the one baked into this build it runs `npm install -g @usagefleet/cli@<version>`
|
|
148
|
-
and re-runs `install` to restart the service on it. `usagefleet update` does the
|
|
149
|
-
same on demand.
|
|
46
|
+
Completions are printed, not installed — send them where your shell looks:
|
|
150
47
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
48
|
+
```bash
|
|
49
|
+
usagefleet completion zsh > ~/.zsh/completions/_usagefleet # a dir on your fpath
|
|
50
|
+
usagefleet completion fish > ~/.config/fish/completions/usagefleet.fish
|
|
51
|
+
```
|
|
154
52
|
|
|
155
|
-
|
|
156
|
-
plain semver, npm missing, npm exiting non-zero (a root-owned global prefix is
|
|
157
|
-
the usual cause) and locally-built (`dev`) builds all leave the install as it
|
|
158
|
-
was — the service is only restarted after npm reports success. Set
|
|
159
|
-
`USAGEFLEET_UPDATE=0` to turn the check off.
|
|
53
|
+
## What it collects
|
|
160
54
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
55
|
+
- **Token usage** — Claude Code (`~/.claude/projects/**/*.jsonl`), Claude
|
|
56
|
+
Desktop agent-mode (Cowork) sessions, and the
|
|
57
|
+
[pi](https://github.com/badlogic/pi-mono) agent (`~/.pi/agent/sessions`,
|
|
58
|
+
Anthropic-provider records only; other providers don't touch Claude limits).
|
|
59
|
+
A per-file byte offset in the config file means each line is sent once;
|
|
60
|
+
rotation and truncation are handled and partial lines are never sent. Delivery
|
|
61
|
+
is at-least-once — the server dedups on `uuid`.
|
|
62
|
+
- **Your real limit %** — the collector uses the Claude login already on the
|
|
63
|
+
machine (subscription OAuth from `claude`: macOS login Keychain, elsewhere
|
|
64
|
+
`~/.claude/.credentials.json`; falling back to `ANTHROPIC_API_KEY`), sends a
|
|
65
|
+
1-token ping to the Messages API, and reads Anthropic's
|
|
66
|
+
`anthropic-ratelimit-unified-5h/7d-utilization` headers. Credentials never
|
|
67
|
+
leave the machine — only the percentages do. `usagefleet status` shows which
|
|
68
|
+
login was found.
|
|
164
69
|
|
|
165
|
-
|
|
70
|
+
Uploaded per record: token counts, model, session id, hostname, working
|
|
71
|
+
directory, git branch. Prompts, responses and file contents are never read.
|
|
166
72
|
|
|
167
|
-
|
|
168
|
-
this machine and reports your true utilization — no keys pasted anywhere:
|
|
73
|
+
## Desktop notifications
|
|
169
74
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
75
|
+
The first time a window crosses a threshold (default `80` and `95`) you get a
|
|
76
|
+
desktop notification — once per threshold per window, re-armed when the window
|
|
77
|
+
resets. macOS uses `osascript`, Linux `notify-send` (falling back to
|
|
78
|
+
`kdialog --passivepopup`; install `libnotify-bin` if neither exists), Windows a
|
|
79
|
+
WinRT toast via `powershell.exe`.
|
|
174
80
|
|
|
175
|
-
|
|
176
|
-
`
|
|
177
|
-
the percentages to the server. `usagefleet status` shows which login was found.
|
|
178
|
-
The token/credentials never leave your machine — only the resulting percentages
|
|
179
|
-
are uploaded.
|
|
81
|
+
`usagefleet notify-test` confirms it works. Tune with
|
|
82
|
+
`USAGEFLEET_NOTIFY_THRESHOLDS="50,80,95"`, disable with `USAGEFLEET_NOTIFY=0`.
|
|
180
83
|
|
|
181
|
-
|
|
84
|
+
## Blocking prompts over the limit
|
|
182
85
|
|
|
183
86
|
A group can be set to **refuse new prompts** once it has burned its budget slice
|
|
184
|
-
(1/
|
|
185
|
-
off by default
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
UPDATE groups SET block_on_session_limit = true WHERE name = 'Backend'; -- 5h window
|
|
189
|
-
UPDATE groups SET block_on_weekly_limit = true WHERE name = 'Backend'; -- 7d window
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
Enforcement is a Claude Code `UserPromptSubmit` hook, registered in
|
|
193
|
-
`~/.claude/settings.json` automatically by `usagefleet install` (and removed
|
|
194
|
-
by `usagefleet uninstall`). Re-running install refreshes the path instead of
|
|
195
|
-
stacking a second hook; a settings file that doesn't parse is left untouched.
|
|
196
|
-
Set `USAGEFLEET_HOOK=0` to keep your settings file out of it and add the hook
|
|
197
|
-
yourself:
|
|
87
|
+
(1/N of the account limit) for a window — a switch per window on the Groups
|
|
88
|
+
page, both off by default. `usagefleet install` registers a Claude Code `UserPromptSubmit` hook in
|
|
89
|
+
`~/.claude/settings.json` (removed by `uninstall`, refreshed rather than stacked
|
|
90
|
+
on re-install, and skipped entirely with `USAGEFLEET_HOOK=0`):
|
|
198
91
|
|
|
199
92
|
```json
|
|
200
93
|
{
|
|
@@ -206,79 +99,81 @@ yourself:
|
|
|
206
99
|
}
|
|
207
100
|
```
|
|
208
101
|
|
|
209
|
-
`
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
Only whole prompts are blocked, never tool calls mid-turn, so the current turn
|
|
214
|
-
always finishes.
|
|
102
|
+
`guard` **fails open** everywhere else — no config, server down, timeout (5s),
|
|
103
|
+
old server, 429 — because a tracker problem must never stop you working. Only
|
|
104
|
+
whole prompts are blocked, never tool calls mid-turn, so the current turn always
|
|
105
|
+
finishes.
|
|
215
106
|
|
|
216
|
-
|
|
107
|
+
## Configuration
|
|
217
108
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
109
|
+
`~/.config/usagefleet/config.json` (honours `XDG_CONFIG_HOME`) holds everything
|
|
110
|
+
the CLI persists: your settings plus two machine-managed sections, `state` (tail
|
|
111
|
+
offsets) and `notify` (which thresholds already fired). Delete it to start
|
|
112
|
+
clean. Re-running `install` merges, so rotating a token doesn't reset offsets.
|
|
222
113
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
114
|
+
Env vars override the file:
|
|
115
|
+
|
|
116
|
+
| Variable | Meaning |
|
|
117
|
+
|----------|---------|
|
|
118
|
+
| `USAGEFLEET_ENDPOINT` | server base URL (default `https://usagefleet.com`). Must be `https://`, loopback may be `http://` |
|
|
119
|
+
| `USAGEFLEET_TOKEN` | device token |
|
|
120
|
+
| `USAGEFLEET_PROJECTS` | override `~/.claude/projects` |
|
|
121
|
+
| `USAGEFLEET_DESKTOP` | override the Claude Desktop sessions dir; `off` to skip it |
|
|
122
|
+
| `USAGEFLEET_PI` | override the pi sessions dirs, comma-separated; `off` to skip |
|
|
123
|
+
| `USAGEFLEET_INTERVAL` | watch poll seconds (default 15) |
|
|
124
|
+
| `USAGEFLEET_LIMITS_INTERVAL` | seconds between limit pings (default 300, so the 1-token ping isn't every cycle) |
|
|
125
|
+
| `USAGEFLEET_NOTIFY` | desktop notifications, on by default |
|
|
126
|
+
| `USAGEFLEET_NOTIFY_THRESHOLDS` | utilization % that trigger an alert (default `80,95`) |
|
|
127
|
+
| `USAGEFLEET_BATCH` | records per upload (default 100, server caps at 1000) |
|
|
128
|
+
| `USAGEFLEET_CONFIG` | override the config file path |
|
|
129
|
+
| `USAGEFLEET_UPDATE` | `0` turns the self-update check off |
|
|
130
|
+
| `USAGEFLEET_UPDATE_INTERVAL` | seconds between update checks (default `21600` = 6h) |
|
|
131
|
+
| `USAGEFLEET_HOOK` | `0` keeps the prompt-blocking hook out of `~/.claude/settings.json` |
|
|
226
132
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
falls back to KDE's `kdialog --passivepopup`. Install `notify-send` via
|
|
230
|
-
`libnotify` (e.g. `apt install libnotify-bin`) if neither is present.
|
|
231
|
-
- **Windows** — a WinRT toast via built-in `powershell.exe` → Action Center (no
|
|
232
|
-
extra install; it appears under "Windows PowerShell"). Check Settings →
|
|
233
|
-
Notifications if nothing shows up.
|
|
133
|
+
When run as a service, `install` bakes every `USAGEFLEET_*` value currently set
|
|
134
|
+
(plus `ANTHROPIC_API_KEY`) into the launchd/systemd unit, written mode `600`.
|
|
234
135
|
|
|
235
|
-
|
|
136
|
+
## Background service
|
|
236
137
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
138
|
+
`install` is idempotent and reload-safe: re-running it rewrites the service
|
|
139
|
+
definition and restarts it, so it doubles as the update step. It launches an
|
|
140
|
+
absolute `node` plus the installed package path, so an empty service PATH is
|
|
141
|
+
fine — but removing that Node version (`nvm uninstall`) stops the collector
|
|
142
|
+
until you re-run `usagefleet install` under the new one.
|
|
143
|
+
|
|
144
|
+
- **macOS** — a LaunchAgent (`~/Library/LaunchAgents`, RunAtLoad + KeepAlive),
|
|
145
|
+
booted immediately. Logs in `~/Library/Logs/usagefleet/`. Plist is mode `600`:
|
|
146
|
+
it holds your token.
|
|
147
|
+
- **Linux** — a `systemd --user` unit, plus `loginctl enable-linger $USER` so it
|
|
148
|
+
survives logout. If `systemctl` can't be driven it prints the manual steps.
|
|
149
|
+
- **Windows** — a Scheduled Task (`usagefleet`) starting at logon, restarting on
|
|
150
|
+
failure, running hidden through a `wscript` launcher in
|
|
151
|
+
`%LOCALAPPDATA%\usagefleet` (which carries the env values set at install
|
|
152
|
+
time). Log: `%LOCALAPPDATA%\usagefleet\usagefleet.log`. Inspect with
|
|
153
|
+
`schtasks /query /tn usagefleet /v /fo list`.
|
|
154
|
+
|
|
155
|
+
> **macOS limits under the service.** Usage collection works headless. Reading
|
|
156
|
+
> the login Keychain for the real limit % may be denied to a non-interactive
|
|
157
|
+
> agent — the collector logs a clear hint. Either approve `/usr/bin/security`
|
|
158
|
+
> access to the `Claude Code-credentials` item once, or set `ANTHROPIC_API_KEY`
|
|
159
|
+
> before `usagefleet install` so limits use the API key.
|
|
241
160
|
|
|
242
|
-
|
|
243
|
-
> session, so notifications appear normally. On Linux a `systemd --user` service
|
|
244
|
-
> needs access to your session bus (`DBUS_SESSION_BUS_ADDRESS`) for `notify-send`
|
|
245
|
-
> to reach the notification daemon — typical for `--user` units in a graphical
|
|
246
|
-
> login. `usagefleet install` bakes `USAGEFLEET_NOTIFY*` into the unit.
|
|
161
|
+
## Updates
|
|
247
162
|
|
|
248
|
-
|
|
163
|
+
`watch` checks the npm registry at startup and every 6 hours; on a new version
|
|
164
|
+
it runs `npm install -g @usagefleet/cli@<version>` and re-runs `install` to
|
|
165
|
+
restart the service on it. `usagefleet update` does the same on demand. npm is
|
|
166
|
+
called through the absolute path next to the running `node`, because a
|
|
167
|
+
launchd/systemd service gets a minimal PATH.
|
|
168
|
+
|
|
169
|
+
Every failure is a no-op — registry unreachable, non-semver version, npm
|
|
170
|
+
missing or exiting non-zero, local `dev` builds — and the service is only
|
|
171
|
+
restarted after npm reports success.
|
|
172
|
+
|
|
173
|
+
## From source
|
|
249
174
|
|
|
250
175
|
```bash
|
|
251
|
-
|
|
252
|
-
|
|
176
|
+
cd apps/cli
|
|
177
|
+
bun install
|
|
178
|
+
bun run src/index.ts status # or: npm run build && node dist/index.js status
|
|
253
179
|
```
|
|
254
|
-
|
|
255
|
-
`install` is idempotent and reload-safe: re-running it rewrites the service
|
|
256
|
-
definition and restarts it, so it doubles as the update step. The service is
|
|
257
|
-
launched as an absolute `node` plus the installed package path, so an empty
|
|
258
|
-
service PATH is fine — but removing that Node version (`nvm uninstall`) stops
|
|
259
|
-
the collector until you re-run `usagefleet install` under the new one.
|
|
260
|
-
|
|
261
|
-
- **macOS** — installs a LaunchAgent (`~/Library/LaunchAgents`, RunAtLoad +
|
|
262
|
-
KeepAlive) and boots it (bootout → bootstrap → kickstart). Logs at
|
|
263
|
-
`~/Library/Logs/usagefleet/usagefleet.*.log` (not `/tmp`, which is
|
|
264
|
-
world-writable). The plist is written mode `600`: it holds your token.
|
|
265
|
-
- **Linux** — writes a `--user` unit and runs `systemctl --user daemon-reload`,
|
|
266
|
-
`enable --now`, `restart`, plus `loginctl enable-linger $USER` automatically so
|
|
267
|
-
it survives logout. If `systemctl` can't be driven, it prints the manual steps.
|
|
268
|
-
- **Windows** — registers a Scheduled Task (`usagefleet`) that starts at logon,
|
|
269
|
-
restarts on failure, and runs **hidden** (no console window) through a
|
|
270
|
-
generated `wscript` launcher in `%LOCALAPPDATA%\usagefleet`. Task XML has no
|
|
271
|
-
env support, so the launcher carries the `USAGEFLEET_*` values that were set
|
|
272
|
-
when you ran `install`, and redirects output to
|
|
273
|
-
`%LOCALAPPDATA%\usagefleet\usagefleet.log` (truncated on each start).
|
|
274
|
-
Inspect it with `schtasks /query /tn usagefleet /v /fo list`.
|
|
275
|
-
|
|
276
|
-
The OS is reported automatically (`process.platform` → `mac`/`linux`/`windows`).
|
|
277
|
-
|
|
278
|
-
> **macOS limits under the service.** Usage collection (reading JSONL files) works
|
|
279
|
-
> headless. The **real limit %** feature reads the login Keychain, and a
|
|
280
|
-
> non-interactive launchd agent may be denied that read — the collector logs a
|
|
281
|
-
> clear hint when this happens. If it does, either approve `/usr/bin/security`
|
|
282
|
-
> access to the `Claude Code-credentials` item once, or set `ANTHROPIC_API_KEY`
|
|
283
|
-
> before `usagefleet install` (it's baked into the service) so limits use the
|
|
284
|
-
> API key instead.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/** The command list, shared by `help` and the generated completion scripts, so
|
|
2
|
+
* a new command can't land in one and be missing from the other. `args` is the
|
|
3
|
+
* hint `help` prints after the name; completion only needs the bare name. */
|
|
4
|
+
export const commands = [
|
|
5
|
+
{ name: 'run', meaning: 'scan once, upload usage + report limits' },
|
|
6
|
+
{ name: 'watch', args: '[--interval s]', meaning: 'poll continuously (default 15s)' },
|
|
7
|
+
{ name: 'limits', meaning: 'report only your real 5h/weekly usage' },
|
|
8
|
+
{ name: 'guard', meaning: 'exit 2 when the group is over a blocking limit' },
|
|
9
|
+
{ name: 'update', meaning: 'update to the latest release now' },
|
|
10
|
+
{ name: 'notify-test', meaning: 'fire a test desktop notification' },
|
|
11
|
+
{ name: 'status', meaning: 'service health, limits, resolved config' },
|
|
12
|
+
{ name: 'config', meaning: 'config file location and env overrides' },
|
|
13
|
+
{ name: 'completion', args: '<zsh|fish>', meaning: 'print a shell completion script' },
|
|
14
|
+
{ name: 'version', meaning: 'print the release version' },
|
|
15
|
+
{ name: 'install', args: '--token <t>', meaning: 'configure + install the service and prompt guard' },
|
|
16
|
+
{ name: 'uninstall', meaning: 'remove the service and the guard' },
|
|
17
|
+
];
|
|
18
|
+
export const shells = ['zsh', 'fish'];
|
|
19
|
+
/** A completion script for `shell`, on stdout — the user decides where it goes
|
|
20
|
+
* (`> ~/.zsh/completions/_usagefleet`, `> ~/.config/fish/completions/…`).
|
|
21
|
+
* Writing those files ourselves would mean guessing at fpath and rc files. */
|
|
22
|
+
export function completionScript(shell) {
|
|
23
|
+
return shell === 'zsh' ? zsh() : fish();
|
|
24
|
+
}
|
|
25
|
+
function zsh() {
|
|
26
|
+
// zsh splits a _describe entry on the first colon, so any colon in the text
|
|
27
|
+
// has to be escaped or the description gets cut in half.
|
|
28
|
+
const rows = commands.map(c => `\t\t'${c.name}:${c.meaning.replaceAll(':', '\\:')}'`).join('\n');
|
|
29
|
+
return `#compdef usagefleet
|
|
30
|
+
|
|
31
|
+
_usagefleet() {
|
|
32
|
+
local -a commands
|
|
33
|
+
commands=(
|
|
34
|
+
${rows}
|
|
35
|
+
)
|
|
36
|
+
if (( CURRENT == 2 )); then
|
|
37
|
+
_describe 'command' commands
|
|
38
|
+
elif [[ $words[2] == completion ]]; then
|
|
39
|
+
_values 'shell' ${shells.join(' ')}
|
|
40
|
+
fi
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
_usagefleet "$@"
|
|
44
|
+
`;
|
|
45
|
+
}
|
|
46
|
+
function fish() {
|
|
47
|
+
const quote = (s) => s.replaceAll("'", "\\'");
|
|
48
|
+
const rows = commands
|
|
49
|
+
.map(c => `complete -c usagefleet -n __fish_use_subcommand -a ${c.name} -d '${quote(c.meaning)}'`)
|
|
50
|
+
.join('\n');
|
|
51
|
+
// -f: no file completion, this CLI takes no paths.
|
|
52
|
+
return `complete -c usagefleet -f
|
|
53
|
+
${rows}
|
|
54
|
+
complete -c usagefleet -n '__fish_seen_subcommand_from completion' -a '${shells.join(' ')}'
|
|
55
|
+
`;
|
|
56
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { detectClaudeCreds } from './claude-creds.js';
|
|
3
3
|
import { reportLimitsOnce, runOnce } from './collector.js';
|
|
4
|
+
import { commands, completionScript, shells } from './completion.js';
|
|
4
5
|
import { loadConfig } from './config.js';
|
|
5
6
|
import { runGuard } from './guard.js';
|
|
6
7
|
import { loadNotifyConfig } from './notifier.js';
|
|
@@ -248,24 +249,21 @@ function cmdConfig() {
|
|
|
248
249
|
console.log('');
|
|
249
250
|
console.log(hint('`usagefleet status` shows the resolved values'));
|
|
250
251
|
}
|
|
252
|
+
/** Print a completion script, or the shells we know how to write one for. */
|
|
253
|
+
function cmdCompletion() {
|
|
254
|
+
const shell = process.argv[3];
|
|
255
|
+
if (!shells.includes(shell)) {
|
|
256
|
+
console.error(fail('completion', `expected one of ${shells.join(', ')}`));
|
|
257
|
+
process.exitCode = 1;
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
console.log(completionScript(shell));
|
|
261
|
+
}
|
|
251
262
|
/** Command list, in the same padded-column style as the result lines. */
|
|
252
263
|
function help() {
|
|
253
|
-
const commands = [
|
|
254
|
-
['run', 'scan once, upload usage + report limits'],
|
|
255
|
-
['watch [--interval s]', 'poll continuously (default 15s)'],
|
|
256
|
-
['limits', 'report only your real 5h/weekly usage'],
|
|
257
|
-
['guard', 'exit 2 when the group is over a blocking limit'],
|
|
258
|
-
['update', 'update to the latest release now'],
|
|
259
|
-
['notify-test', 'fire a test desktop notification'],
|
|
260
|
-
['status', 'service health, limits, resolved config'],
|
|
261
|
-
['config', 'config file location and env overrides'],
|
|
262
|
-
['version', 'print the release version'],
|
|
263
|
-
['install --token <t>', 'configure + install the service and prompt guard'],
|
|
264
|
-
['uninstall', 'remove the service and the guard'],
|
|
265
|
-
];
|
|
266
264
|
console.log(header());
|
|
267
265
|
console.log('');
|
|
268
|
-
print(commands);
|
|
266
|
+
print(commands.map(c => [c.args ? `${c.name} ${c.args}` : c.name, c.meaning]));
|
|
269
267
|
console.log('');
|
|
270
268
|
console.log(hint('`usagefleet config` lists the config file and its env overrides'));
|
|
271
269
|
}
|
|
@@ -309,6 +307,9 @@ async function main() {
|
|
|
309
307
|
case 'config': {
|
|
310
308
|
return cmdConfig();
|
|
311
309
|
}
|
|
310
|
+
case 'completion': {
|
|
311
|
+
return cmdCompletion();
|
|
312
|
+
}
|
|
312
313
|
// Bare version, so the installer can compare builds without parsing help.
|
|
313
314
|
case 'version':
|
|
314
315
|
case '--version':
|
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.72";
|