ccclub 0.2.35 → 0.2.36
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/index.js +68 -13
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -18,16 +18,28 @@ import { randomBytes } from "crypto";
|
|
|
18
18
|
import { execSync } from "child_process";
|
|
19
19
|
|
|
20
20
|
// ../shared/dist/constants.js
|
|
21
|
-
var BLOCK_DURATION_HOURS =
|
|
21
|
+
var BLOCK_DURATION_HOURS = 1;
|
|
22
22
|
var BLOCK_DURATION_MS = BLOCK_DURATION_HOURS * 60 * 60 * 1e3;
|
|
23
23
|
var DEFAULT_API_URL = "https://ccclub.dev";
|
|
24
24
|
var CLAUDE_PROJECTS_DIR = ".claude/projects";
|
|
25
25
|
var CCCLUB_CONFIG_DIR = ".ccclub";
|
|
26
|
+
var PLAN_PRICES = {
|
|
27
|
+
pro: 20,
|
|
28
|
+
max100: 100,
|
|
29
|
+
max200: 200,
|
|
30
|
+
api: 0
|
|
31
|
+
};
|
|
32
|
+
var PLAN_LABELS = {
|
|
33
|
+
pro: "Pro $20",
|
|
34
|
+
max100: "Max $100",
|
|
35
|
+
max200: "Max $200",
|
|
36
|
+
api: "API"
|
|
37
|
+
};
|
|
26
38
|
var MODEL_PRICING = {
|
|
27
|
-
// Opus 4.5+
|
|
39
|
+
// Opus 4.5+
|
|
28
40
|
"claude-opus-4-6": { input: 5, output: 25, cacheCreation: 6.25, cacheRead: 0.5 },
|
|
29
41
|
"claude-opus-4-5-20251101": { input: 5, output: 25, cacheCreation: 6.25, cacheRead: 0.5 },
|
|
30
|
-
// Opus 4.1
|
|
42
|
+
// Opus 4.0–4.1
|
|
31
43
|
"claude-opus-4-1-20250805": { input: 15, output: 75, cacheCreation: 18.75, cacheRead: 1.5 },
|
|
32
44
|
// Sonnet
|
|
33
45
|
"claude-sonnet-4-5-20250929": { input: 3, output: 15, cacheCreation: 3.75, cacheRead: 0.3 },
|
|
@@ -37,8 +49,23 @@ var MODEL_PRICING = {
|
|
|
37
49
|
"claude-haiku-4-5-20251001": { input: 1, output: 5, cacheCreation: 1.25, cacheRead: 0.1 },
|
|
38
50
|
"claude-3-5-haiku-20241022": { input: 0.8, output: 4, cacheCreation: 1, cacheRead: 0.08 }
|
|
39
51
|
};
|
|
52
|
+
var FAMILY_FALLBACK = {
|
|
53
|
+
opus: MODEL_PRICING["claude-opus-4-6"],
|
|
54
|
+
sonnet: MODEL_PRICING["claude-sonnet-4-5-20250929"],
|
|
55
|
+
haiku: MODEL_PRICING["claude-haiku-4-5-20251001"]
|
|
56
|
+
};
|
|
57
|
+
function getPricing(model) {
|
|
58
|
+
if (MODEL_PRICING[model])
|
|
59
|
+
return MODEL_PRICING[model];
|
|
60
|
+
const lower = model.toLowerCase();
|
|
61
|
+
for (const family of Object.keys(FAMILY_FALLBACK)) {
|
|
62
|
+
if (lower.includes(family))
|
|
63
|
+
return FAMILY_FALLBACK[family];
|
|
64
|
+
}
|
|
65
|
+
return FAMILY_FALLBACK.sonnet;
|
|
66
|
+
}
|
|
40
67
|
function calculateCost(model, inputTokens, outputTokens, cacheCreationTokens, cacheReadTokens) {
|
|
41
|
-
const pricing =
|
|
68
|
+
const pricing = getPricing(model);
|
|
42
69
|
return (inputTokens * pricing.input + outputTokens * pricing.output + cacheCreationTokens * pricing.cacheCreation + cacheReadTokens * pricing.cacheRead) / 1e6;
|
|
43
70
|
}
|
|
44
71
|
|
|
@@ -751,10 +778,19 @@ function printGroup(data, code, period, config, showCache = false) {
|
|
|
751
778
|
${data.group.name}`));
|
|
752
779
|
console.log(chalk5.dim(` ${period.toUpperCase()} \xB7 ${data.start.slice(0, 10)} \u2192 ${data.end.slice(0, 10)} \xB7 ${data.group.memberCount} members
|
|
753
780
|
`));
|
|
781
|
+
const hasPlan = data.rankings.some((r) => r.plan);
|
|
782
|
+
const head = ["#", "Name", "Tokens", "Cost"];
|
|
783
|
+
const widths = [5, 20, 10, 12];
|
|
784
|
+
if (hasPlan) {
|
|
785
|
+
head.push("Plan \xB7 ROI");
|
|
786
|
+
widths.push(16);
|
|
787
|
+
}
|
|
788
|
+
head.push("Chats");
|
|
789
|
+
widths.push(8);
|
|
754
790
|
const table = new Table({
|
|
755
|
-
head:
|
|
791
|
+
head: head.map((h) => chalk5.cyan(h)),
|
|
756
792
|
style: { head: [], border: [] },
|
|
757
|
-
colWidths:
|
|
793
|
+
colWidths: widths
|
|
758
794
|
});
|
|
759
795
|
for (const entry of data.rankings) {
|
|
760
796
|
const isMe = entry.userId === config.userId;
|
|
@@ -763,13 +799,29 @@ function printGroup(data, code, period, config, showCache = false) {
|
|
|
763
799
|
const id = (s) => s;
|
|
764
800
|
const c = isMe ? chalk5.green : entry.rank === 1 ? chalk5.yellow : id;
|
|
765
801
|
const nameC = isMe ? chalk5.green.bold : entry.rank === 1 ? chalk5.yellow.bold : id;
|
|
766
|
-
|
|
802
|
+
const row = [
|
|
767
803
|
`${marker}${c(String(entry.rank))}`,
|
|
768
804
|
nameC(entry.displayName),
|
|
769
805
|
c(formatTokens(tokens)),
|
|
770
|
-
c(`$${entry.costUSD.toFixed(2)}`)
|
|
771
|
-
|
|
772
|
-
|
|
806
|
+
c(`$${entry.costUSD.toFixed(2)}`)
|
|
807
|
+
];
|
|
808
|
+
if (hasPlan) {
|
|
809
|
+
if (entry.plan && entry.plan !== "api") {
|
|
810
|
+
const price = PLAN_PRICES[entry.plan];
|
|
811
|
+
const label = PLAN_LABELS[entry.plan] || entry.plan;
|
|
812
|
+
const monthly = entry.monthlyCostUSD || 0;
|
|
813
|
+
const roi = price > 0 ? Math.round(monthly / price * 100) : 0;
|
|
814
|
+
const roiStr = `${roi}%`;
|
|
815
|
+
const roiC = roi >= 100 ? chalk5.green.bold(roiStr) : roi >= 50 ? chalk5.yellow(roiStr) : chalk5.dim(roiStr);
|
|
816
|
+
row.push(`${c(label)} ${roiC}`);
|
|
817
|
+
} else if (entry.plan === "api") {
|
|
818
|
+
row.push(c("API"));
|
|
819
|
+
} else {
|
|
820
|
+
row.push(chalk5.dim("\u2014"));
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
row.push(c(String(entry.chatCount)));
|
|
824
|
+
table.push(row);
|
|
773
825
|
}
|
|
774
826
|
console.log(table.toString());
|
|
775
827
|
console.log(chalk5.dim(` Dashboard: ${config.apiUrl}/g/${code}`));
|
|
@@ -839,7 +891,7 @@ import chalk6 from "chalk";
|
|
|
839
891
|
import ora5 from "ora";
|
|
840
892
|
async function profileCommand(options) {
|
|
841
893
|
const config = await requireConfig();
|
|
842
|
-
const hasUpdate = options.name !== void 0 || options.avatar !== void 0 || options.public || options.private;
|
|
894
|
+
const hasUpdate = options.name !== void 0 || options.avatar !== void 0 || options.public || options.private || options.plan !== void 0;
|
|
843
895
|
if (!hasUpdate) {
|
|
844
896
|
const spinner2 = ora5("Fetching profile...").start();
|
|
845
897
|
try {
|
|
@@ -857,6 +909,7 @@ async function profileCommand(options) {
|
|
|
857
909
|
console.log(` Name: ${profile.displayName}`);
|
|
858
910
|
console.log(` Avatar: ${profile.avatar || chalk6.dim("(default)")}`);
|
|
859
911
|
console.log(` Visibility: ${profile.visibility === "public" ? chalk6.green("public") : chalk6.dim("private")}`);
|
|
912
|
+
console.log(` Plan: ${profile.plan ? PLAN_LABELS[profile.plan] || profile.plan : chalk6.dim("(not set)")}`);
|
|
860
913
|
console.log();
|
|
861
914
|
} catch (err) {
|
|
862
915
|
spinner2.fail(`Error: ${err instanceof Error ? err.message : err}`);
|
|
@@ -868,6 +921,7 @@ async function profileCommand(options) {
|
|
|
868
921
|
if (options.avatar !== void 0) body.avatar = options.avatar;
|
|
869
922
|
if (options.public) body.visibility = "public";
|
|
870
923
|
if (options.private) body.visibility = "private";
|
|
924
|
+
if (options.plan !== void 0) body.plan = options.plan;
|
|
871
925
|
const spinner = ora5("Updating profile...").start();
|
|
872
926
|
try {
|
|
873
927
|
const res = await fetch(`${config.apiUrl}/api/profile`, {
|
|
@@ -893,6 +947,7 @@ async function profileCommand(options) {
|
|
|
893
947
|
console.log(` Name: ${profile.displayName}`);
|
|
894
948
|
console.log(` Avatar: ${profile.avatar || chalk6.dim("(default)")}`);
|
|
895
949
|
console.log(` Visibility: ${profile.visibility === "public" ? chalk6.green("public") : chalk6.dim("private")}`);
|
|
950
|
+
console.log(` Plan: ${profile.plan ? PLAN_LABELS[profile.plan] || profile.plan : chalk6.dim("(not set)")}`);
|
|
896
951
|
console.log();
|
|
897
952
|
} catch (err) {
|
|
898
953
|
spinner.fail(`Error: ${err instanceof Error ? err.message : err}`);
|
|
@@ -998,7 +1053,7 @@ async function hookCommand() {
|
|
|
998
1053
|
}
|
|
999
1054
|
|
|
1000
1055
|
// src/index.ts
|
|
1001
|
-
var VERSION = "0.2.
|
|
1056
|
+
var VERSION = "0.2.36";
|
|
1002
1057
|
startUpdateCheck(VERSION);
|
|
1003
1058
|
var program = new Command();
|
|
1004
1059
|
program.name("ccclub").description("CCClub - Compare Claude Code usage with friends").version(VERSION);
|
|
@@ -1007,7 +1062,7 @@ program.command("join").description("Join a friend's group").argument("<invite-c
|
|
|
1007
1062
|
program.command("sync").description("Sync local usage data to server").option("-s, --silent", "No output (used by auto-sync hook)").option("-f, --full", "Force full re-sync of all data").action(syncCommand);
|
|
1008
1063
|
program.command("rank", { isDefault: true, hidden: true }).description("Show leaderboard").option("-p, --period <period>", "daily | weekly | monthly | all-time", "daily").option("-g, --group <code>", "Group invite code").option("--global", "Show global public ranking").option("--cache", "Include cache tokens in count").action(rankCommand);
|
|
1009
1064
|
program.command("-p weekly|monthly|all-time", { hidden: false }).description("Switch period (default: daily)");
|
|
1010
|
-
program.command("profile").description("View or update your profile").option("-n, --name <name>", "Set display name").option("--avatar <url>", "Set avatar URL (empty string to reset)").option("--public", "Set profile visibility to public").option("--private", "Set profile visibility to private").action(profileCommand);
|
|
1065
|
+
program.command("profile").description("View or update your profile").option("-n, --name <name>", "Set display name").option("--avatar <url>", "Set avatar URL (empty string to reset)").option("--public", "Set profile visibility to public").option("--private", "Set profile visibility to private").option("--plan <plan>", "Set subscription plan (pro, max100, max200, api, or empty to clear)").action(profileCommand);
|
|
1011
1066
|
program.command("create").description("Create a new group").action(createGroupCommand);
|
|
1012
1067
|
program.command("show-data").description("Show exactly what data CCClub uploads (privacy audit)").action(showDataCommand);
|
|
1013
1068
|
program.command("hook").description("Set up Claude Code hook for auto-sync on session end").action(hookCommand);
|