ccgather 1.3.36 → 1.3.38

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.
Files changed (2) hide show
  1. package/dist/index.js +43 -9
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -230,6 +230,7 @@ init_config();
230
230
  var fs2 = __toESM(require("fs"));
231
231
  var path2 = __toESM(require("path"));
232
232
  var os2 = __toESM(require("os"));
233
+ var crypto = __toESM(require("crypto"));
233
234
 
234
235
  // src/lib/credentials.ts
235
236
  var fs = __toESM(require("fs"));
@@ -373,6 +374,33 @@ function estimateCost(model, inputTokens, outputTokens, cacheWriteTokens = 0, ca
373
374
  const cacheReadCost = cacheReadTokens / 1e6 * price.cacheRead;
374
375
  return Math.round((inputCost + outputCost + cacheWriteCost + cacheReadCost) * 100) / 100;
375
376
  }
377
+ function generateSessionHash(filePath, maxLines = 50) {
378
+ try {
379
+ const content = fs2.readFileSync(filePath, "utf-8");
380
+ const lines = content.split("\n").slice(0, maxLines).join("\n");
381
+ const fileName = path2.basename(filePath);
382
+ const hashInput = `${fileName}:${lines}`;
383
+ return crypto.createHash("sha256").update(hashInput).digest("hex");
384
+ } catch {
385
+ return null;
386
+ }
387
+ }
388
+ function generateSessionFingerprint(sessionFiles) {
389
+ const sessionHashes = [];
390
+ for (const file of sessionFiles) {
391
+ const hash = generateSessionHash(file);
392
+ if (hash) {
393
+ sessionHashes.push(hash);
394
+ }
395
+ }
396
+ sessionHashes.sort();
397
+ const combinedHash = crypto.createHash("sha256").update(sessionHashes.join(":")).digest("hex");
398
+ return {
399
+ sessionHashes,
400
+ combinedHash,
401
+ sessionCount: sessionHashes.length
402
+ };
403
+ }
376
404
  function scanUsageData(options = {}) {
377
405
  const currentProjectDir = getCurrentProjectDir();
378
406
  if (!currentProjectDir) {
@@ -505,6 +533,7 @@ function scanUsageData(options = {}) {
505
533
  models: data.models
506
534
  })).sort((a, b) => a.date.localeCompare(b.date));
507
535
  const credentials = readCredentials();
536
+ const sessionFingerprint = generateSessionFingerprint(jsonlFiles);
508
537
  return {
509
538
  version: CCGATHER_JSON_VERSION,
510
539
  lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
@@ -529,7 +558,8 @@ function scanUsageData(options = {}) {
529
558
  account: {
530
559
  ccplan: credentials.ccplan,
531
560
  rateLimitTier: credentials.rateLimitTier
532
- }
561
+ },
562
+ sessionFingerprint
533
563
  };
534
564
  }
535
565
  function getSessionFileCount() {
@@ -549,7 +579,7 @@ function getCurrentProjectName() {
549
579
 
550
580
  // src/lib/ui.ts
551
581
  var import_chalk = __toESM(require("chalk"));
552
- var VERSION = true ? "1.3.36" : "0.0.0";
582
+ var VERSION = true ? "1.3.38" : "0.0.0";
553
583
  var colors = {
554
584
  primary: import_chalk.default.hex("#DA7756"),
555
585
  // Claude coral
@@ -902,7 +932,8 @@ function ccgatherToUsageData(data) {
902
932
  lastUsed: data.stats.lastUsed,
903
933
  ccplan: data.account?.ccplan || null,
904
934
  rateLimitTier: data.account?.rateLimitTier || null,
905
- dailyUsage: data.dailyUsage || []
935
+ dailyUsage: data.dailyUsage || [],
936
+ sessionFingerprint: data.sessionFingerprint
906
937
  };
907
938
  }
908
939
  async function submitToServer(data) {
@@ -930,7 +961,9 @@ async function submitToServer(data) {
930
961
  ccplan: data.ccplan,
931
962
  rateLimitTier: data.rateLimitTier,
932
963
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
933
- dailyUsage: data.dailyUsage
964
+ dailyUsage: data.dailyUsage,
965
+ // Session fingerprint for duplicate prevention
966
+ sessionFingerprint: data.sessionFingerprint
934
967
  })
935
968
  });
936
969
  if (!response.ok) {
@@ -1022,6 +1055,7 @@ async function verifyToken() {
1022
1055
  }
1023
1056
  async function submit(options) {
1024
1057
  console.log(header("Submit Usage Data", "\u{1F4E4}"));
1058
+ const config = getConfig();
1025
1059
  const verifySpinner = (0, import_ora2.default)({
1026
1060
  text: "Verifying authentication...",
1027
1061
  color: "cyan"
@@ -1029,6 +1063,9 @@ async function submit(options) {
1029
1063
  const tokenCheck = await verifyToken();
1030
1064
  if (!tokenCheck.valid) {
1031
1065
  verifySpinner.stop();
1066
+ config.delete("apiToken");
1067
+ config.delete("userId");
1068
+ config.delete("username");
1032
1069
  console.log();
1033
1070
  console.log(` ${colors.warning("\u{1F510}")} ${colors.muted("Authentication required")}`);
1034
1071
  console.log();
@@ -1044,13 +1081,11 @@ async function submit(options) {
1044
1081
  const { auth: auth2 } = await Promise.resolve().then(() => (init_auth(), auth_exports));
1045
1082
  await auth2({});
1046
1083
  console.log();
1047
- console.log(` ${colors.muted("Please run")} ${colors.primary("npx ccgather")} ${colors.muted("again to submit.")}`);
1048
- console.log();
1084
+ return submit(options);
1049
1085
  }
1050
- process.exit(0);
1086
+ return;
1051
1087
  }
1052
1088
  verifySpinner.succeed(colors.success("Authenticated"));
1053
- const config = getConfig();
1054
1089
  const username = tokenCheck.username || config.get("username");
1055
1090
  if (username) {
1056
1091
  console.log(`
@@ -1386,7 +1421,6 @@ async function showMainMenu() {
1386
1421
  if (startAuth) {
1387
1422
  const { auth: auth2 } = await Promise.resolve().then(() => (init_auth(), auth_exports));
1388
1423
  await auth2({});
1389
- console.log();
1390
1424
  } else {
1391
1425
  console.log(colors.dim("\n Goodbye!\n"));
1392
1426
  process.exit(0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccgather",
3
- "version": "1.3.36",
3
+ "version": "1.3.38",
4
4
  "description": "CLI tool for syncing Claude Code usage data to CCgather leaderboard",
5
5
  "bin": {
6
6
  "ccgather": "dist/index.js",
@@ -28,7 +28,7 @@
28
28
  "chalk": "^5.3.0",
29
29
  "commander": "^12.1.0",
30
30
  "conf": "^13.0.1",
31
- "inquirer": "^10.2.2",
31
+ "inquirer": "^9.2.23",
32
32
  "open": "^10.1.0",
33
33
  "ora": "^8.1.0",
34
34
  "update-notifier": "^7.3.1"