@usagefleet/cli 1.2.74 → 1.2.75

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.
@@ -0,0 +1,34 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { claudeStatePath } from './paths.js';
3
+ /** Pull the account out of a parsed `~/.claude.json`. Everything but the uuid is
4
+ * cosmetic, so a missing display field yields null rather than no account. */
5
+ export function parseClaudeAccount(raw) {
6
+ if (typeof raw !== 'object' || raw === null) {
7
+ return null;
8
+ }
9
+ const acc = raw.oauthAccount;
10
+ if (typeof acc !== 'object' || acc === null) {
11
+ return null;
12
+ }
13
+ const { accountUuid, emailAddress, organizationName } = acc;
14
+ if (typeof accountUuid !== 'string' || accountUuid === '') {
15
+ return null;
16
+ }
17
+ const str = (v) => (typeof v === 'string' && v !== '' ? v.slice(0, 200) : null);
18
+ return { email: str(emailAddress), extId: accountUuid.slice(0, 100), org: str(organizationName) };
19
+ }
20
+ /**
21
+ * Read the local Claude Code login. Purely a local file read — no network, no
22
+ * credentials — so it is safe to call on every limits cycle. Returns null when
23
+ * Claude Code has never signed in on this machine (an ANTHROPIC_API_KEY-only
24
+ * setup has no account identity at all); those devices fall back to the
25
+ * server's unidentified-account bucket, i.e. today's behaviour.
26
+ */
27
+ export function detectClaudeAccount(path = claudeStatePath()) {
28
+ try {
29
+ return parseClaudeAccount(JSON.parse(readFileSync(path, 'utf-8')));
30
+ }
31
+ catch {
32
+ return null;
33
+ }
34
+ }
package/dist/collector.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { existsSync } from 'node:fs';
2
2
  import { hostname } from 'node:os';
3
3
  import { sep } from 'node:path';
4
+ import { detectClaudeAccount } from './claude-account.js';
4
5
  import { detectClaudeCreds, macKeychainDenied } from './claude-creds.js';
5
6
  import { fetchLimits } from './claude-limits.js';
6
7
  import { maybeNotify } from './notifier.js';
@@ -243,7 +244,11 @@ export async function reportLimitsOnce(cfg, log = () => {
243
244
  // Same vocabulary as the usage leg: a rejection the operator can act on has to
244
245
  // say which one it is, since this runs every cycle and an anonymous failure
245
246
  // would repeat forever without ever naming the fix.
246
- const outcome = await postLimits(report, cfg);
247
+ // Tag the reading with the Claude account it came from, so a fleet split over
248
+ // two subscriptions gets two independent budgets instead of one row both
249
+ // machines overwrite. Subscription logins only — an API key has no account.
250
+ const account = report.source === 'sub' ? detectClaudeAccount() : null;
251
+ const outcome = await postLimits({ ...report, account }, cfg);
247
252
  if (outcome === 'plan') {
248
253
  log('warn', planWall(cfg.endpoint));
249
254
  }
package/dist/index.js CHANGED
@@ -1,7 +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, installCompletions, removeCompletions, shells, } from './completion.js';
4
+ import { commands, completionScript, installCompletions, removeCompletions, shells } from './completion.js';
5
5
  import { loadConfig } from './config.js';
6
6
  import { runGuard } from './guard.js';
7
7
  import { loadNotifyConfig } from './notifier.js';
package/dist/paths.js CHANGED
@@ -46,3 +46,10 @@ export function defaultPiSessionsDirs() {
46
46
  export function claudeSettingsPath() {
47
47
  return join(process.env.CLAUDE_CONFIG_DIR ?? join(homedir(), '.claude'), 'settings.json');
48
48
  }
49
+ /** Claude Code's global state file (`~/.claude.json`), which records which
50
+ * Anthropic account this machine is logged into. Unlike settings.json it sits
51
+ * next to the config dir, not inside it. */
52
+ export function claudeStatePath() {
53
+ const dir = process.env.CLAUDE_CONFIG_DIR;
54
+ return dir ? join(dir, '.claude.json') : join(homedir(), '.claude.json');
55
+ }
package/dist/release.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // Generated by .github/workflows/release.yml.
2
- export const RELEASE_VERSION = "1.2.74";
2
+ export const RELEASE_VERSION = "1.2.75";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usagefleet/cli",
3
- "version": "1.2.74",
3
+ "version": "1.2.75",
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",