@usagefleet/cli 1.2.169 → 1.2.171
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 +4 -2
- package/dist/collector.js +3 -0
- package/dist/config.js +2 -1
- package/dist/index.js +3 -2
- package/dist/parser.js +51 -0
- package/dist/paths.js +4 -0
- package/dist/release.js +1 -1
- package/dist/store.js +1 -0
- package/dist/tailer.js +10 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -78,8 +78,9 @@ shell once. `uninstall` removes both again, and self-update keeps them current.
|
|
|
78
78
|
Desktop agent-mode (Cowork) sessions, and the
|
|
79
79
|
[pi](https://github.com/badlogic/pi-mono) agent (`~/.pi/agent/sessions`,
|
|
80
80
|
`anthropic` and `claude-bridge` provider records only; other providers don't
|
|
81
|
-
touch Claude limits),
|
|
82
|
-
(`~/.pj/sessions`),
|
|
81
|
+
touch Claude limits), the jcode agent (`~/.jcode/sessions`) and its pj fork
|
|
82
|
+
(`~/.pj/sessions`), and the agent CLI (`~/.agents/sessions`), Claude
|
|
83
|
+
subscription turns only.
|
|
83
84
|
A per-file byte offset in the config file means each line is sent once;
|
|
84
85
|
rotation and truncation are handled and partial lines are never sent. Delivery
|
|
85
86
|
is at-least-once — the server dedups on `uuid`.
|
|
@@ -160,6 +161,7 @@ it (`usagefleet config` prints this same table):
|
|
|
160
161
|
| `piDir` | `USAGEFLEET_PI` | pi sessions dirs — string or array in the file, comma-separated in env; `off` to skip |
|
|
161
162
|
| `jcodeDir` | `USAGEFLEET_JCODE` | override the jcode sessions dir (`~/.jcode/sessions`); `off` to skip it |
|
|
162
163
|
| `pjDir` | `USAGEFLEET_PJ` | override the pj sessions dir (`~/.pj/sessions`); `off` to skip it |
|
|
164
|
+
| `agentDir` | `USAGEFLEET_AGENT` | override the agent CLI sessions dir (`~/.agents/sessions`); `off` to skip it |
|
|
163
165
|
| `interval` | `USAGEFLEET_INTERVAL` | watch poll seconds (default 15) |
|
|
164
166
|
| `limitsInterval` | `USAGEFLEET_LIMITS_INTERVAL` | seconds between limit reports (default 60; 300 on API keys, where each report costs a 1-token ping) |
|
|
165
167
|
| `notifications` | `USAGEFLEET_NOTIFY` | desktop notifications, on by default (`false`/`0` disables) |
|
package/dist/collector.js
CHANGED
|
@@ -43,6 +43,9 @@ export async function runOnce(cfg, log = () => {
|
|
|
43
43
|
for (const dir of cfg.piDirs) {
|
|
44
44
|
roots.push({ dir, onlyProjects: false, source: 'pi' });
|
|
45
45
|
}
|
|
46
|
+
if (cfg.agentDir) {
|
|
47
|
+
roots.push({ dir: cfg.agentDir, onlyProjects: false, source: 'agent' });
|
|
48
|
+
}
|
|
46
49
|
const files = [];
|
|
47
50
|
for (const root of roots) {
|
|
48
51
|
for (const fp of listJsonlFiles(root.dir, dirCache)) {
|
package/dist/config.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { defaultDesktopSessionsDir, defaultJcodeSessionsDir, defaultPiSessionsDirs, defaultPjSessionsDir, defaultProjectsDir, } from './paths.js';
|
|
1
|
+
import { defaultAgentSessionsDir, defaultDesktopSessionsDir, defaultJcodeSessionsDir, defaultPiSessionsDirs, defaultPjSessionsDir, defaultProjectsDir, } from './paths.js';
|
|
2
2
|
import { readStore, storePath } from './store.js';
|
|
3
3
|
/** Matches the server's BatchSchema `.max(1000)`. */
|
|
4
4
|
const MAX_BATCH = 1000;
|
|
@@ -24,6 +24,7 @@ export function loadConfig() {
|
|
|
24
24
|
// Kept even when the env names a token: the service unit bakes the default
|
|
25
25
|
// one into USAGEFLEET_TOKEN, and bindings are what route around it.
|
|
26
26
|
accountTokens: file.accountTokens ?? {},
|
|
27
|
+
agentDir: resolveOptionalDir(process.env.USAGEFLEET_AGENT, file.agentDir, defaultAgentSessionsDir()),
|
|
27
28
|
batchSize,
|
|
28
29
|
desktopDir: resolveOptionalDir(process.env.USAGEFLEET_DESKTOP, file.desktopDir, defaultDesktopSessionsDir()),
|
|
29
30
|
jcodeDir: resolveOptionalDir(process.env.USAGEFLEET_JCODE, file.jcodeDir, defaultJcodeSessionsDir()),
|
package/dist/index.js
CHANGED
|
@@ -89,7 +89,7 @@ async function cmdWatch() {
|
|
|
89
89
|
const rawUpdate = positiveNumber(process.env.USAGEFLEET_UPDATE_INTERVAL, settings.updateInterval) ?? 6 * 60 * 60;
|
|
90
90
|
const updateInterval = Math.max(60, rawUpdate) * 1000;
|
|
91
91
|
let lastUpdateAt = 0;
|
|
92
|
-
const watching = [cfg.projectsDir, cfg.desktopDir, ...cfg.piDirs, cfg.jcodeDir, cfg.pjDir].filter((d) => !!d);
|
|
92
|
+
const watching = [cfg.projectsDir, cfg.desktopDir, ...cfg.piDirs, cfg.jcodeDir, cfg.pjDir, cfg.agentDir].filter((d) => !!d);
|
|
93
93
|
console.log(header(`watching every ${interval / 1000}s`));
|
|
94
94
|
console.log(hint(`${watching.map(tilde).join(' · ')} → ${host(ENDPOINT)}`));
|
|
95
95
|
console.log('');
|
|
@@ -193,7 +193,7 @@ async function cmdStatus() {
|
|
|
193
193
|
console.log('');
|
|
194
194
|
console.log(row('endpoint', host(ENDPOINT)));
|
|
195
195
|
console.log(row('device', `${state.deviceId} · token ${tokenFor(cfg, detectClaudeAccount()?.extId).slice(0, 8)}…`));
|
|
196
|
-
const watching = [cfg.projectsDir, cfg.desktopDir, ...cfg.piDirs, cfg.jcodeDir, cfg.pjDir].filter((d) => !!d);
|
|
196
|
+
const watching = [cfg.projectsDir, cfg.desktopDir, ...cfg.piDirs, cfg.jcodeDir, cfg.pjDir, cfg.agentDir].filter((d) => !!d);
|
|
197
197
|
for (const [i, dir] of watching.entries()) {
|
|
198
198
|
console.log(row(i === 0 ? 'watching' : '', tilde(dir)));
|
|
199
199
|
}
|
|
@@ -285,6 +285,7 @@ function cmdConfig() {
|
|
|
285
285
|
['piDir', 'USAGEFLEET_PI', 'pi session dirs · string or array; env comma-separated'],
|
|
286
286
|
['jcodeDir', 'USAGEFLEET_JCODE', 'override ~/.jcode/sessions ("off" disables)'],
|
|
287
287
|
['pjDir', 'USAGEFLEET_PJ', 'override ~/.pj/sessions ("off" disables)'],
|
|
288
|
+
['agentDir', 'USAGEFLEET_AGENT', 'override ~/.agents/sessions ("off" disables)'],
|
|
288
289
|
['interval', 'USAGEFLEET_INTERVAL', 'watch interval seconds (default 15)'],
|
|
289
290
|
[
|
|
290
291
|
'limitsInterval',
|
package/dist/parser.js
CHANGED
|
@@ -71,6 +71,57 @@ function parsePiLine(o, sessionCwd) {
|
|
|
71
71
|
version: null,
|
|
72
72
|
};
|
|
73
73
|
}
|
|
74
|
+
/** agent CLI session line: `{type:"usage", id, provider, account, model, timestamp,
|
|
75
|
+
* input, output, cache_read, cache_write}`, one per request, `timestamp` in epoch ms.
|
|
76
|
+
* Only provider "anthropic" with an `account` (the Claude account uuid; API keys
|
|
77
|
+
* have none) ran on a subscription, and each line books on its own account because
|
|
78
|
+
* the agent switches accounts between requests. Lines from agents that predate
|
|
79
|
+
* `id` and `account` cannot be told apart from API-key requests and are skipped.
|
|
80
|
+
* /clone and /worktree copy these lines into a new session, so `id` (the API's
|
|
81
|
+
* response id) is the dedup key, not anything per file. */
|
|
82
|
+
export function parseAgentLine(line, sessionCwd, sessionId) {
|
|
83
|
+
let parsed;
|
|
84
|
+
try {
|
|
85
|
+
parsed = JSON.parse(line);
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
if (typeof parsed !== 'object' || parsed === null) {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
const o = parsed;
|
|
94
|
+
const id = str(o.id);
|
|
95
|
+
const account = str(o.account);
|
|
96
|
+
const at = new Date(typeof o.timestamp === 'number' ? o.timestamp : Number.NaN);
|
|
97
|
+
if (o.type !== 'usage' || o.provider !== 'anthropic' || !id || !account || Number.isNaN(at.getTime())) {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
const tokens = (v) => (typeof v === 'number' ? v : 0);
|
|
101
|
+
return {
|
|
102
|
+
accountExtId: account,
|
|
103
|
+
record: {
|
|
104
|
+
// The agent does not log the cache-write TTL; the server prices it by the user's setting.
|
|
105
|
+
cacheCreation1h: null,
|
|
106
|
+
cacheCreation5m: null,
|
|
107
|
+
cacheCreationTokens: tokens(o.cache_write),
|
|
108
|
+
cacheReadTokens: tokens(o.cache_read),
|
|
109
|
+
cwd: sessionCwd,
|
|
110
|
+
gitBranch: null,
|
|
111
|
+
inputTokens: tokens(o.input),
|
|
112
|
+
messageId: id,
|
|
113
|
+
model: str(o.model),
|
|
114
|
+
outputTokens: tokens(o.output),
|
|
115
|
+
requestId: null,
|
|
116
|
+
serviceTier: null,
|
|
117
|
+
sessionId,
|
|
118
|
+
source: 'agent',
|
|
119
|
+
timestamp: at.toISOString(),
|
|
120
|
+
uuid: `agent:${id}`,
|
|
121
|
+
version: null,
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
}
|
|
74
125
|
/**
|
|
75
126
|
* Parse a single JSONL line. Returns a UsageRecord only for assistant messages
|
|
76
127
|
* that carry a usage object; everything else (user/system/summary/tool/…) → null.
|
package/dist/paths.js
CHANGED
|
@@ -72,6 +72,10 @@ export function defaultJcodeSessionsDir() {
|
|
|
72
72
|
export function defaultPjSessionsDir() {
|
|
73
73
|
return join(homedir(), '.pj', 'sessions');
|
|
74
74
|
}
|
|
75
|
+
/** The agent CLI keeps every session flat in `~/.agents/sessions`. */
|
|
76
|
+
export function defaultAgentSessionsDir() {
|
|
77
|
+
return join(homedir(), '.agents', 'sessions');
|
|
78
|
+
}
|
|
75
79
|
/** pi's agent dir, the root for its sessions and extensions.
|
|
76
80
|
* PI_CODING_AGENT_DIR is pi's own relocation knob; unlike the service-side
|
|
77
81
|
* scan above, the guard installer runs from the user's shell where that env
|
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.171";
|
package/dist/store.js
CHANGED
package/dist/tailer.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { closeSync, openSync, readSync, statSync } from 'node:fs';
|
|
2
|
-
import {
|
|
2
|
+
import { basename } from 'node:path';
|
|
3
|
+
import { parseAgentLine, parseLine } from './parser.js';
|
|
3
4
|
import { dim, line, tilde, yellow } from './ui.js';
|
|
4
5
|
/** Max bytes read from a single file per cycle (bounds memory on huge backlogs). */
|
|
5
6
|
const MAX_READ = 16 * 1024 * 1024;
|
|
@@ -8,7 +9,7 @@ const USAGE_KEY = Buffer.from('"usage"');
|
|
|
8
9
|
// Unescaped quotes: the same text quoted inside a message is `\"customType\"` and never matches.
|
|
9
10
|
const ACCOUNT_KEY = Buffer.from('"customType":"claude-account"');
|
|
10
11
|
/**
|
|
11
|
-
* The working directory of a pi session, read from the file's first line
|
|
12
|
+
* The working directory of a pi or agent session, read from the file's first line
|
|
12
13
|
* (`{"type":"session",…,"cwd":"/path"}`). pi's message lines carry no cwd, and
|
|
13
14
|
* tailing usually resumes past the header, so it is re-read per cycle rather
|
|
14
15
|
* than remembered. The session dir name encodes the same path but lossily
|
|
@@ -68,7 +69,7 @@ export function tailFile(filePath, prev, source = 'cli') {
|
|
|
68
69
|
let sessionCwd = null;
|
|
69
70
|
try {
|
|
70
71
|
readSync(fd, buf, 0, length, start);
|
|
71
|
-
if (source === 'pi') {
|
|
72
|
+
if (source === 'pi' || source === 'agent') {
|
|
72
73
|
sessionCwd = piSessionCwd(fd);
|
|
73
74
|
}
|
|
74
75
|
}
|
|
@@ -106,6 +107,12 @@ export function tailFile(filePath, prev, source = 'cli') {
|
|
|
106
107
|
if (isAccountLine) {
|
|
107
108
|
account = piSessionAccount(text);
|
|
108
109
|
}
|
|
110
|
+
else if (source === 'agent') {
|
|
111
|
+
const usage = parseAgentLine(text, sessionCwd, basename(filePath, '.jsonl'));
|
|
112
|
+
if (usage) {
|
|
113
|
+
addRecord(segments, usage.accountExtId, usage.record);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
109
116
|
else {
|
|
110
117
|
const rec = parseLine(text, source, sessionCwd);
|
|
111
118
|
if (rec) {
|