@usagefleet/cli 1.2.164 → 1.2.165
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/dist/claude-account.js +13 -0
- package/dist/collector.js +51 -13
- package/dist/index.js +2 -1
- package/dist/release.js +1 -1
- package/package.json +1 -1
package/dist/claude-account.js
CHANGED
|
@@ -33,6 +33,19 @@ export function detectClaudeAccount(path = claudeStatePath()) {
|
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
const OAUTH_PROFILE_URL = 'https://api.anthropic.com/api/oauth/profile';
|
|
36
|
+
/** The OAuth access token of the login jcode (or pj) has active in its `auth.json`,
|
|
37
|
+
* falling back to the first saved login the way jcode does. Null when unreadable. */
|
|
38
|
+
export function activeJcodeToken(authPath) {
|
|
39
|
+
try {
|
|
40
|
+
const auth = JSON.parse(readFileSync(authPath, 'utf-8'));
|
|
41
|
+
const accounts = auth.anthropic_accounts ?? [];
|
|
42
|
+
const active = accounts.find(a => a.label === auth.active_anthropic_account) ?? accounts[0];
|
|
43
|
+
return typeof active?.access === 'string' && active.access !== '' ? active.access : null;
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
36
49
|
/** The accountUuid Anthropic says owns this OAuth token, or null when it cannot
|
|
37
50
|
* say. Null fails open to the file's identity: the endpoint is undocumented, and
|
|
38
51
|
* losing it must not stop every collector from reporting limits. */
|
package/dist/collector.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
2
|
import { hostname } from 'node:os';
|
|
3
|
-
import { sep } from 'node:path';
|
|
4
|
-
import { detectClaudeAccount, tokenOwner } from './claude-account.js';
|
|
3
|
+
import { dirname, join, sep } from 'node:path';
|
|
4
|
+
import { activeJcodeToken, detectClaudeAccount, tokenOwner } from './claude-account.js';
|
|
5
5
|
import { detectClaudeCreds, macKeychainDenied } from './claude-creds.js';
|
|
6
6
|
import { fetchLimits } from './claude-limits.js';
|
|
7
7
|
import { ENDPOINT } from './config.js';
|
|
@@ -29,7 +29,7 @@ const LIMITS_CACHE_REFRESH_MS = 10 * 60_000;
|
|
|
29
29
|
*/
|
|
30
30
|
export async function runOnce(cfg, log = () => {
|
|
31
31
|
/* empty */
|
|
32
|
-
}, dirCache = new Map()) {
|
|
32
|
+
}, dirCache = new Map(), tokenOwners = new Map()) {
|
|
33
33
|
const { state } = readStore(cfg.storePath);
|
|
34
34
|
// Each scan root is tagged with the app that owns it. Claude Code's projects
|
|
35
35
|
// dir is scanned whole; the Claude Desktop sessions root is filtered to its
|
|
@@ -52,16 +52,15 @@ export async function runOnce(cfg, log = () => {
|
|
|
52
52
|
files.push({ fp, source: root.source });
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
|
-
const
|
|
56
|
-
{ dir: cfg.jcodeDir, source: 'jcode' },
|
|
57
|
-
{ dir: cfg.pjDir, source: 'pj' },
|
|
58
|
-
];
|
|
59
|
-
const jcodeSessions = jcodeRoots.flatMap(({ dir, source }) => dir ? listJcodeSessions(dir).map(fp => ({ fp, source })) : []);
|
|
55
|
+
const jcodeSessions = await listJcodeSessionsWithAccount(cfg, tokenOwners);
|
|
60
56
|
// Each source yields its new records and how to commit having sent them, so both
|
|
61
57
|
// share one upload path: offsets for appended JSONL, a watermark for jcode and pj.
|
|
62
58
|
const reads = [
|
|
63
59
|
...files.map(({ fp, source }) => ({ fp, read: () => tailPending(fp, source, state.files) })),
|
|
64
|
-
...jcodeSessions.map(({ fp, source }) => ({
|
|
60
|
+
...jcodeSessions.map(({ accountExtId, fp, source }) => ({
|
|
61
|
+
fp,
|
|
62
|
+
read: () => jcodePending(fp, source, accountExtId, state.jcode),
|
|
63
|
+
})),
|
|
65
64
|
];
|
|
66
65
|
const result = {
|
|
67
66
|
accepted: 0,
|
|
@@ -228,7 +227,7 @@ function tailPending(fp, source, files) {
|
|
|
228
227
|
segments: tail.segments,
|
|
229
228
|
};
|
|
230
229
|
}
|
|
231
|
-
function jcodePending(fp, source, sessions) {
|
|
230
|
+
function jcodePending(fp, source, accountExtId, sessions) {
|
|
232
231
|
const session = readJcodeSession(fp, source, sessions[fp]);
|
|
233
232
|
if (!session) {
|
|
234
233
|
return null;
|
|
@@ -237,11 +236,50 @@ function jcodePending(fp, source, sessions) {
|
|
|
237
236
|
commit: () => {
|
|
238
237
|
sessions[fp] = session.nextState;
|
|
239
238
|
},
|
|
240
|
-
|
|
241
|
-
// turns book on the device's Claude Code account; read jcode's active login if they differ.
|
|
242
|
-
segments: session.records.length > 0 ? [{ accountExtId: undefined, records: session.records }] : [],
|
|
239
|
+
segments: session.records.length > 0 ? [{ accountExtId, records: session.records }] : [],
|
|
243
240
|
};
|
|
244
241
|
}
|
|
242
|
+
async function listJcodeSessionsWithAccount(cfg, tokenOwners) {
|
|
243
|
+
const roots = [
|
|
244
|
+
{ dir: cfg.jcodeDir, source: 'jcode' },
|
|
245
|
+
{ dir: cfg.pjDir, source: 'pj' },
|
|
246
|
+
];
|
|
247
|
+
const out = [];
|
|
248
|
+
for (const { dir, source } of roots) {
|
|
249
|
+
const sessions = dir ? listJcodeSessions(dir) : [];
|
|
250
|
+
if (!dir || sessions.length === 0) {
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
const accountExtId = await jcodeAccount(dir, tokenOwners);
|
|
254
|
+
for (const fp of sessions) {
|
|
255
|
+
out.push({ accountExtId, fp, source });
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
return out;
|
|
259
|
+
}
|
|
260
|
+
/** The Anthropic account jcode (or pj) is signed into: the owner of the active login in
|
|
261
|
+
* the `auth.json` beside its sessions dir. Undefined (the batch then books on the
|
|
262
|
+
* Claude Code account) when that file or the profile lookup fails. Owners are cached per
|
|
263
|
+
* access token, so the profile call repeats only when jcode refreshes or switches logins.
|
|
264
|
+
* shortcut: books a turn on the login active at upload time, so a switch between a turn
|
|
265
|
+
* and its upload (or a backfill) books it on the new login; stamp the login per turn in
|
|
266
|
+
* jcode if that matters. */
|
|
267
|
+
async function jcodeAccount(sessionsDir, owners) {
|
|
268
|
+
const token = activeJcodeToken(join(dirname(sessionsDir), 'auth.json'));
|
|
269
|
+
if (!token) {
|
|
270
|
+
return undefined;
|
|
271
|
+
}
|
|
272
|
+
const cached = owners.get(token);
|
|
273
|
+
if (cached) {
|
|
274
|
+
return cached;
|
|
275
|
+
}
|
|
276
|
+
const owner = await tokenOwner(token);
|
|
277
|
+
if (!owner) {
|
|
278
|
+
return undefined;
|
|
279
|
+
}
|
|
280
|
+
owners.set(token, owner);
|
|
281
|
+
return owner;
|
|
282
|
+
}
|
|
245
283
|
/** Drop the progress of jcode sessions no longer on disk; returns whether anything went. */
|
|
246
284
|
function pruneGoneSessions(sessions, listed) {
|
|
247
285
|
const kept = new Set(listed);
|
package/dist/index.js
CHANGED
|
@@ -97,13 +97,14 @@ async function cmdWatch() {
|
|
|
97
97
|
let timer = null;
|
|
98
98
|
let running = false;
|
|
99
99
|
const dirCache = new Map();
|
|
100
|
+
const tokenOwners = new Map();
|
|
100
101
|
const tick = async () => {
|
|
101
102
|
if (stopping) {
|
|
102
103
|
return;
|
|
103
104
|
}
|
|
104
105
|
running = true;
|
|
105
106
|
try {
|
|
106
|
-
const r = await runOnce(cfg, stream, dirCache);
|
|
107
|
+
const r = await runOnce(cfg, stream, dirCache, tokenOwners);
|
|
107
108
|
// Dropped records are real data loss, so they must show up even in a
|
|
108
109
|
// cycle that uploaded nothing.
|
|
109
110
|
if (r.sent > 0 || r.dropped > 0) {
|
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.165";
|