@efoo/ccprofile 0.3.0 → 0.4.0
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/commands/usage.js +58 -15
- package/package.json +1 -1
package/dist/commands/usage.js
CHANGED
|
@@ -4,7 +4,32 @@ import { parseArgs } from "node:util";
|
|
|
4
4
|
import { assertDarwin } from "../lib/keychain.js";
|
|
5
5
|
import { chromeUserAgent, chromeUserDataDir, parseProfiles, readSessionCookies, safeStorageKey, } from "../lib/chrome.js";
|
|
6
6
|
import { fetchAccountUsage, NOT_SIGNED_IN, } from "../lib/claudeai.js";
|
|
7
|
-
import {
|
|
7
|
+
import { loadConfig } from "../lib/config.js";
|
|
8
|
+
import { bold, cyan, dim, fail, red, table, warn, yellow } from "../lib/format.js";
|
|
9
|
+
/**
|
|
10
|
+
* Maps a claude.ai account email to the ccprofile name registered for it, so a
|
|
11
|
+
* signed-in Chrome account that matches a stored profile is labelled with that
|
|
12
|
+
* profile's name. Matching is case-insensitive.
|
|
13
|
+
*/
|
|
14
|
+
function profileNamesByEmail() {
|
|
15
|
+
const byEmail = new Map();
|
|
16
|
+
try {
|
|
17
|
+
for (const [name, entry] of Object.entries(loadConfig().profiles)) {
|
|
18
|
+
if (entry.email)
|
|
19
|
+
byEmail.set(entry.email.toLowerCase(), name);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
// A malformed/unsupported config.json shouldn't sink the usage table;
|
|
24
|
+
// profile labelling is auxiliary, so degrade to unlabelled accounts.
|
|
25
|
+
}
|
|
26
|
+
return byEmail;
|
|
27
|
+
}
|
|
28
|
+
function matchedProfileName(email, byEmail) {
|
|
29
|
+
if (email === null)
|
|
30
|
+
return null;
|
|
31
|
+
return byEmail.get(email.toLowerCase()) ?? null;
|
|
32
|
+
}
|
|
8
33
|
export async function usageCommand(argv) {
|
|
9
34
|
const { values } = parseArgs({
|
|
10
35
|
args: argv,
|
|
@@ -32,11 +57,12 @@ export async function usageCommand(argv) {
|
|
|
32
57
|
: startSpinner(`querying claude.ai for ${profiles.length} Chrome profile(s)…`);
|
|
33
58
|
const results = await Promise.all(profiles.map((profile) => loadUsage(profile, userDataDir, key, userAgent)));
|
|
34
59
|
spinner.stop();
|
|
60
|
+
const byEmail = profileNamesByEmail();
|
|
35
61
|
if (values.json) {
|
|
36
|
-
console.log(JSON.stringify(results.map(toJson), null, 2));
|
|
62
|
+
console.log(JSON.stringify(results.map((r) => toJson(r, byEmail)), null, 2));
|
|
37
63
|
return hasRealFailure(results) ? 1 : 0;
|
|
38
64
|
}
|
|
39
|
-
return render(results);
|
|
65
|
+
return render(results, byEmail);
|
|
40
66
|
}
|
|
41
67
|
/**
|
|
42
68
|
* A profile that was never signed in is expected and not a failure; anything
|
|
@@ -57,19 +83,12 @@ async function loadUsage(profile, userDataDir, key, userAgent) {
|
|
|
57
83
|
return { profile, usage: { ok: false, status: 0, detail } };
|
|
58
84
|
}
|
|
59
85
|
}
|
|
60
|
-
function render(results) {
|
|
61
|
-
const
|
|
62
|
-
const rows = [header];
|
|
86
|
+
function render(results, byEmail) {
|
|
87
|
+
const signedIn = [];
|
|
63
88
|
const problems = [];
|
|
64
89
|
for (const { profile, usage } of results) {
|
|
65
90
|
if (usage.ok) {
|
|
66
|
-
|
|
67
|
-
usage.email ?? dim("(unknown)"),
|
|
68
|
-
dim(profile.name),
|
|
69
|
-
windowCell(usage.report.session),
|
|
70
|
-
windowCell(usage.report.weeklyAll),
|
|
71
|
-
windowCell(usage.report.fable),
|
|
72
|
-
]);
|
|
91
|
+
signedIn.push({ profile, usage });
|
|
73
92
|
}
|
|
74
93
|
else if (usage.detail !== NOT_SIGNED_IN) {
|
|
75
94
|
// A missing session is expected for stray Chrome profiles; only real
|
|
@@ -77,6 +96,19 @@ function render(results) {
|
|
|
77
96
|
problems.push(warn(`${profile.name}: ${usage.detail}`));
|
|
78
97
|
}
|
|
79
98
|
}
|
|
99
|
+
const header = ["PROFILE", "ACCOUNT", "CHROME", "5-HOUR", "WEEK · ALL", "FABLE · WEEK"].map(bold);
|
|
100
|
+
const rows = [header];
|
|
101
|
+
for (const { profile, usage } of sortByWeeklyReset(signedIn)) {
|
|
102
|
+
const name = matchedProfileName(usage.email, byEmail);
|
|
103
|
+
rows.push([
|
|
104
|
+
name === null ? dim("-") : cyan(name),
|
|
105
|
+
usage.email ?? dim("(unknown)"),
|
|
106
|
+
dim(profile.name),
|
|
107
|
+
windowCell(usage.report.session),
|
|
108
|
+
windowCell(usage.report.weeklyAll),
|
|
109
|
+
windowCell(usage.report.fable),
|
|
110
|
+
]);
|
|
111
|
+
}
|
|
80
112
|
if (rows.length === 1 && problems.length === 0) {
|
|
81
113
|
console.log(dim("No claude.ai sessions found in Chrome. Sign in at https://claude.ai and retry."));
|
|
82
114
|
return 0;
|
|
@@ -87,6 +119,15 @@ function render(results) {
|
|
|
87
119
|
console.log(problem);
|
|
88
120
|
return problems.length > 0 ? 1 : 0;
|
|
89
121
|
}
|
|
122
|
+
/**
|
|
123
|
+
* Orders accounts by how soon their weekly (all-models) limit resets — the
|
|
124
|
+
* nearest reset first, the furthest last. Accounts with no weekly window sort
|
|
125
|
+
* to the end so a resolved figure never sits below a blank one.
|
|
126
|
+
*/
|
|
127
|
+
function sortByWeeklyReset(results) {
|
|
128
|
+
const resetKey = (r) => r.usage.report.weeklyAll?.resetsAt?.getTime() ?? Number.MAX_SAFE_INTEGER;
|
|
129
|
+
return [...results].sort((a, b) => resetKey(a) - resetKey(b));
|
|
130
|
+
}
|
|
90
131
|
function windowCell(window) {
|
|
91
132
|
if (window === null)
|
|
92
133
|
return dim("-");
|
|
@@ -111,12 +152,14 @@ function formatReset(date) {
|
|
|
111
152
|
const md = `${date.getMonth() + 1}/${date.getDate()}`.padStart(5, " ");
|
|
112
153
|
return `${md} ${pad(date.getHours())}:${pad(date.getMinutes())}`;
|
|
113
154
|
}
|
|
114
|
-
function toJson(result) {
|
|
155
|
+
function toJson(result, byEmail) {
|
|
115
156
|
const { profile, usage } = result;
|
|
157
|
+
const email = usage.ok ? usage.email : null;
|
|
116
158
|
return {
|
|
159
|
+
profile: matchedProfileName(email, byEmail),
|
|
117
160
|
chromeProfile: profile.name,
|
|
118
161
|
chromeDir: profile.dir,
|
|
119
|
-
email
|
|
162
|
+
email,
|
|
120
163
|
error: usage.ok ? null : usage.detail,
|
|
121
164
|
usage: usage.ok ? serializeReport(usage.report) : null,
|
|
122
165
|
};
|
package/package.json
CHANGED