@usagefleet/cli 1.2.129 → 1.2.131
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 -0
- package/dist/config.js +9 -0
- package/dist/guard.js +3 -2
- package/dist/index.js +14 -3
- package/dist/release.js +1 -1
- package/dist/store.js +1 -0
- package/dist/uploader.js +3 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -21,6 +21,25 @@ first report, not at login), sets the collector to start with your session and
|
|
|
21
21
|
writes `~/.config/usagefleet/config.json` (mode `600`). The dashboard fills in
|
|
22
22
|
within a minute.
|
|
23
23
|
|
|
24
|
+
### One machine, several Claude accounts
|
|
25
|
+
|
|
26
|
+
If you switch this machine between Claude accounts that belong to different
|
|
27
|
+
fleets (a work subscription and a personal one), run `login` once per account,
|
|
28
|
+
each time while signed into that account in Claude Code:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
claude /login # sign into the work account
|
|
32
|
+
usagefleet login uf_work # token from the work fleet
|
|
33
|
+
claude /login # sign into the personal account
|
|
34
|
+
usagefleet login uf_personal # token from your own fleet
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
`login` binds its token to the Claude account signed in at that moment, and
|
|
38
|
+
the collector reports each account's usage and limits with its own token. An
|
|
39
|
+
account without a binding uses the last token you logged in with. Without this,
|
|
40
|
+
whatever you do on the personal account lands in the work fleet and vanishes
|
|
41
|
+
from yours. `usagefleet status` shows which token the current account uses.
|
|
42
|
+
|
|
24
43
|
`login` takes the token and nothing else. The collector reports to
|
|
25
44
|
`usagefleet.com` and there is no way to redirect it: the request carries your
|
|
26
45
|
device token and a log of what this machine is working on.
|
package/dist/config.js
CHANGED
|
@@ -21,6 +21,9 @@ export function loadConfig() {
|
|
|
21
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
|
+
// Kept even when the env names a token: the service unit bakes the default
|
|
25
|
+
// one into USAGEFLEET_TOKEN, and bindings are what route around it.
|
|
26
|
+
accountTokens: file.accountTokens ?? {},
|
|
24
27
|
batchSize,
|
|
25
28
|
desktopDir: resolveOptionalDir(process.env.USAGEFLEET_DESKTOP, file.desktopDir, defaultDesktopSessionsDir()),
|
|
26
29
|
piDirs: resolvePiDirs(process.env.USAGEFLEET_PI, file.piDir),
|
|
@@ -29,6 +32,12 @@ export function loadConfig() {
|
|
|
29
32
|
token,
|
|
30
33
|
};
|
|
31
34
|
}
|
|
35
|
+
/** The token that reports what `extId` (a Claude accountUuid) metered: the one
|
|
36
|
+
* `login` bound to that account, else the default. Without this a machine that
|
|
37
|
+
* switches Claude accounts files one account's usage in the other's fleet. */
|
|
38
|
+
export function tokenFor(cfg, extId) {
|
|
39
|
+
return (extId && cfg.accountTokens[extId]) || cfg.token;
|
|
40
|
+
}
|
|
32
41
|
/** env → file → nothing, for the numeric knobs (intervals, batch): an env
|
|
33
42
|
* value wins when present (empty string counts as unset, same `||` rule as
|
|
34
43
|
* loadConfig), and a non-positive or non-numeric candidate is skipped rather
|
package/dist/guard.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { detectClaudeAccount } from './claude-account.js';
|
|
2
|
+
import { ENDPOINT, loadConfig, tokenFor } from './config.js';
|
|
2
3
|
/** Hooks run on the interactive path — a slow/hung server must not stall a
|
|
3
4
|
* prompt for long. On timeout we fail open (see {@link runGuard}). */
|
|
4
5
|
const TIMEOUT_MS = 5000;
|
|
@@ -39,7 +40,7 @@ export async function runGuard() {
|
|
|
39
40
|
let view;
|
|
40
41
|
try {
|
|
41
42
|
const res = await fetch(`${ENDPOINT}/api/v1/limits`, {
|
|
42
|
-
headers: { 'x-api-key': cfg
|
|
43
|
+
headers: { 'x-api-key': tokenFor(cfg, detectClaudeAccount()?.extId) },
|
|
43
44
|
signal: AbortSignal.timeout(TIMEOUT_MS),
|
|
44
45
|
});
|
|
45
46
|
if (!res.ok) {
|
package/dist/index.js
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { detectClaudeAccount } from './claude-account.js';
|
|
2
3
|
import { detectClaudeCreds } from './claude-creds.js';
|
|
3
4
|
import { reportLimitsOnce, runOnce } from './collector.js';
|
|
4
5
|
import { commands, completionScript, installCompletions, removeCompletions, shells, suggest } from './completion.js';
|
|
5
|
-
import { ENDPOINT, loadConfig, positiveNumber } from './config.js';
|
|
6
|
+
import { ENDPOINT, loadConfig, positiveNumber, tokenFor } from './config.js';
|
|
6
7
|
import { runGuard } from './guard.js';
|
|
7
8
|
import { loadNotifyConfig } from './notifier.js';
|
|
8
9
|
import { sendNotification } from './notify.js';
|
|
9
10
|
import { detectOs } from './os.js';
|
|
10
11
|
import { RELEASE_VERSION } from './release.js';
|
|
11
12
|
import { serviceStatus } from './service.js';
|
|
12
|
-
import { readStore, storePath } from './store.js';
|
|
13
|
+
import { readStore, storePath, updateStore } from './store.js';
|
|
13
14
|
import { ago, bar, blue, dim, fail, green, header, hint, host, line, note, pct, row, state as stateLine, step, tilde, warn, yellow, } from './ui.js';
|
|
14
15
|
import { checkForUpdate } from './update.js';
|
|
15
16
|
function flag(name) {
|
|
@@ -185,7 +186,7 @@ async function cmdStatus() {
|
|
|
185
186
|
: stateLine('warn', 'limits', `no reading yet ${dim('· run `usagefleet limits`')}`));
|
|
186
187
|
console.log('');
|
|
187
188
|
console.log(row('endpoint', host(ENDPOINT)));
|
|
188
|
-
console.log(row('device', `${state.deviceId} · token ${cfg.
|
|
189
|
+
console.log(row('device', `${state.deviceId} · token ${tokenFor(cfg, detectClaudeAccount()?.extId).slice(0, 8)}…`));
|
|
189
190
|
const watching = [cfg.projectsDir, cfg.desktopDir, ...cfg.piDirs].filter((d) => !!d);
|
|
190
191
|
for (const [i, dir] of watching.entries()) {
|
|
191
192
|
console.log(row(i === 0 ? 'watching' : '', tilde(dir)));
|
|
@@ -222,6 +223,16 @@ async function cmdLogin() {
|
|
|
222
223
|
const token = process.argv.slice(3).find(a => !a.startsWith('-')) ?? flag('token');
|
|
223
224
|
if (token) {
|
|
224
225
|
process.env.USAGEFLEET_TOKEN = token;
|
|
226
|
+
// Before install(), so the service it starts already routes this account here.
|
|
227
|
+
// Logging in once per Claude account is how a machine that switches between
|
|
228
|
+
// accounts in different fleets keeps each one's usage in its own fleet.
|
|
229
|
+
const account = detectClaudeAccount();
|
|
230
|
+
if (account) {
|
|
231
|
+
updateStore(storePath(), store => {
|
|
232
|
+
store.accountTokens = { ...store.accountTokens, [account.extId]: token };
|
|
233
|
+
});
|
|
234
|
+
console.log(step('claude account', `${account.email ?? account.extId} reports with this token`));
|
|
235
|
+
}
|
|
225
236
|
}
|
|
226
237
|
const { install } = await import('./service.js');
|
|
227
238
|
install();
|
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.131";
|
package/dist/store.js
CHANGED
package/dist/uploader.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ENDPOINT } from './config.js';
|
|
1
|
+
import { ENDPOINT, tokenFor } from './config.js';
|
|
2
2
|
const MAX_ATTEMPTS = 6;
|
|
3
3
|
const REQUEST_TIMEOUT_MS = 15_000;
|
|
4
4
|
function sleep(ms) {
|
|
@@ -19,7 +19,7 @@ export async function uploadBatch(payload, cfg) {
|
|
|
19
19
|
body: JSON.stringify(payload),
|
|
20
20
|
headers: {
|
|
21
21
|
'content-type': 'application/json',
|
|
22
|
-
'x-api-key': cfg.
|
|
22
|
+
'x-api-key': tokenFor(cfg, payload.accountExtId),
|
|
23
23
|
},
|
|
24
24
|
method: 'POST',
|
|
25
25
|
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
|
|
@@ -90,7 +90,7 @@ export async function postLimits(report, cfg) {
|
|
|
90
90
|
body: JSON.stringify(report),
|
|
91
91
|
headers: {
|
|
92
92
|
'content-type': 'application/json',
|
|
93
|
-
'x-api-key': cfg.
|
|
93
|
+
'x-api-key': tokenFor(cfg, report.account?.extId),
|
|
94
94
|
},
|
|
95
95
|
method: 'POST',
|
|
96
96
|
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
|