ccgather 2.0.33 → 2.0.35

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 +58 -13
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -253,7 +253,7 @@ var init_ui = __esm({
253
253
  "use strict";
254
254
  import_chalk = __toESM(require("chalk"));
255
255
  import_string_width = __toESM(require("string-width"));
256
- VERSION = true ? "2.0.33" : "0.0.0";
256
+ VERSION = true ? "2.0.35" : "0.0.0";
257
257
  colors = {
258
258
  primary: import_chalk.default.hex("#DA7756"),
259
259
  // Claude coral
@@ -489,19 +489,45 @@ var import_ora2 = __toESM(require("ora"));
489
489
  var import_inquirer2 = __toESM(require("inquirer"));
490
490
  init_config();
491
491
 
492
+ // src/lib/device.ts
493
+ var os = __toESM(require("os"));
494
+ var crypto = __toESM(require("crypto"));
495
+ init_config();
496
+ var DEVICE_ID_LENGTH = 16;
497
+ function getDeviceId() {
498
+ try {
499
+ const config = getConfig();
500
+ const existing = config.get("deviceId");
501
+ if (existing) {
502
+ return existing;
503
+ }
504
+ const salt = crypto.randomBytes(8).toString("hex");
505
+ const raw = `${os.hostname() || "unknown"}:${os.homedir()}:${os.platform()}:${os.arch()}:${salt}`;
506
+ const deviceId = crypto.createHash("sha256").update(raw).digest("hex").slice(0, DEVICE_ID_LENGTH);
507
+ try {
508
+ config.set("deviceId", deviceId);
509
+ } catch {
510
+ }
511
+ return deviceId;
512
+ } catch {
513
+ const raw = `${os.hostname() || "unknown"}:${os.homedir()}:${os.platform()}:${os.arch()}`;
514
+ return crypto.createHash("sha256").update(raw).digest("hex").slice(0, DEVICE_ID_LENGTH);
515
+ }
516
+ }
517
+
492
518
  // src/lib/ccgather-json.ts
493
519
  var fs3 = __toESM(require("fs"));
494
520
  var path3 = __toESM(require("path"));
495
- var os3 = __toESM(require("os"));
496
- var crypto = __toESM(require("crypto"));
521
+ var os4 = __toESM(require("os"));
522
+ var crypto2 = __toESM(require("crypto"));
497
523
 
498
524
  // src/lib/credentials.ts
499
525
  var fs = __toESM(require("fs"));
500
526
  var path = __toESM(require("path"));
501
- var os = __toESM(require("os"));
527
+ var os2 = __toESM(require("os"));
502
528
  var import_child_process = require("child_process");
503
529
  function getCredentialsPath() {
504
- return path.join(os.homedir(), ".claude", ".credentials.json");
530
+ return path.join(os2.homedir(), ".claude", ".credentials.json");
505
531
  }
506
532
  function readFromMacKeychain() {
507
533
  try {
@@ -619,7 +645,7 @@ function readCredentials() {
619
645
  // src/lib/pricing.ts
620
646
  var fs2 = __toESM(require("fs"));
621
647
  var path2 = __toESM(require("path"));
622
- var os2 = __toESM(require("os"));
648
+ var os3 = __toESM(require("os"));
623
649
  var LITELLM_PRICING_URL = "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json";
624
650
  var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
625
651
  var FETCH_TIMEOUT_MS = 5e3;
@@ -632,9 +658,9 @@ var FALLBACK_PRICING = {
632
658
  var pricingData = null;
633
659
  function getCacheFilePath() {
634
660
  const configDir = process.platform === "win32" ? path2.join(
635
- process.env.APPDATA || path2.join(os2.homedir(), "AppData", "Roaming"),
661
+ process.env.APPDATA || path2.join(os3.homedir(), "AppData", "Roaming"),
636
662
  "ccgather-nodejs"
637
- ) : path2.join(os2.homedir(), ".config", "ccgather-nodejs");
663
+ ) : path2.join(os3.homedir(), ".config", "ccgather-nodejs");
638
664
  return path2.join(configDir, "pricing-cache.json");
639
665
  }
640
666
  function loadCache() {
@@ -800,7 +826,7 @@ function extractProjectName(filePath) {
800
826
  }
801
827
  function getClaudeProjectsDirs() {
802
828
  const dirs = [];
803
- const home = os3.homedir();
829
+ const home = os4.homedir();
804
830
  const configDir = process.env.CLAUDE_CONFIG_DIR;
805
831
  if (configDir) {
806
832
  const envPath = path3.join(configDir, "projects");
@@ -857,7 +883,7 @@ function generateSessionHash(filePath, maxLines = 50) {
857
883
  const lines = content.split("\n").slice(0, maxLines).join("\n");
858
884
  const fileName = path3.basename(filePath);
859
885
  const hashInput = `${fileName}:${lines}`;
860
- return crypto.createHash("sha256").update(hashInput).digest("hex");
886
+ return crypto2.createHash("sha256").update(hashInput).digest("hex");
861
887
  } catch {
862
888
  return null;
863
889
  }
@@ -871,7 +897,7 @@ function generateSessionFingerprint(sessionFiles) {
871
897
  }
872
898
  }
873
899
  sessionHashes.sort();
874
- const combinedHash = crypto.createHash("sha256").update(sessionHashes.join(":")).digest("hex");
900
+ const combinedHash = crypto2.createHash("sha256").update(sessionHashes.join(":")).digest("hex");
875
901
  return {
876
902
  sessionHashes,
877
903
  combinedHash,
@@ -879,7 +905,7 @@ function generateSessionFingerprint(sessionFiles) {
879
905
  };
880
906
  }
881
907
  function getSessionPathDebugInfo() {
882
- const home = os3.homedir();
908
+ const home = os4.homedir();
883
909
  const cwd = process.cwd();
884
910
  const encodedCwd = encodePathLikeClaude(cwd);
885
911
  const pathsToCheck = [
@@ -1198,7 +1224,9 @@ async function submitToServer(data) {
1198
1224
  sessionFingerprint: data.sessionFingerprint,
1199
1225
  // Opus info for badge display
1200
1226
  hasOpusUsage: data.hasOpusUsage,
1201
- opusModels: data.opusModels
1227
+ opusModels: data.opusModels,
1228
+ // Multi-device support
1229
+ deviceId: getDeviceId()
1202
1230
  })
1203
1231
  });
1204
1232
  if (!response.ok) {
@@ -1437,6 +1465,23 @@ async function submit(options) {
1437
1465
  ` ${colors.muted("Accumulated")} \u26A1 ${colors.primary(formatNumber(prevTokens))} ${colors.dim("\u2502")} \u{1F4B0} ${colors.warning(formatCost(prevCost))}`
1438
1466
  );
1439
1467
  }
1468
+ if (result.deviceInfo && result.deviceInfo.totalDevices > 1) {
1469
+ const di = result.deviceInfo;
1470
+ const otherCount = di.totalDevices - 1;
1471
+ console.log();
1472
+ console.log(sectionHeader("\u{1F5A5}\uFE0F", "Multi-Device"));
1473
+ console.log();
1474
+ console.log(
1475
+ ` ${colors.muted("This PC")} \u26A1 ${colors.primary(formatNumber(di.thisDeviceTokens))} ${colors.dim("\u2502")} \u{1F4B0} ${colors.warning(formatCost(di.thisDeviceCost))}`
1476
+ );
1477
+ console.log(
1478
+ ` ${colors.muted(`+ ${otherCount} other`)} \u26A1 ${colors.primary(formatNumber(di.otherDevicesTokens))} ${colors.dim("\u2502")} \u{1F4B0} ${colors.warning(formatCost(di.otherDevicesCost))}`
1479
+ );
1480
+ console.log(` ${colors.dim("\u2500".repeat(33))}`);
1481
+ console.log(
1482
+ ` ${colors.white.bold("Combined")} \u26A1 ${colors.primary(formatNumber(di.combinedTokens))} ${colors.dim("\u2502")} \u{1F4B0} ${colors.warning(formatCost(di.combinedCost))} ${colors.success("\u2713")}`
1483
+ );
1484
+ }
1440
1485
  if (result.rank || result.countryRank) {
1441
1486
  console.log();
1442
1487
  console.log(sectionHeader("\u{1F4CA}", "Your Ranking"));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccgather",
3
- "version": "2.0.33",
3
+ "version": "2.0.35",
4
4
  "description": "CLI tool for syncing Claude Code usage data to CCgather leaderboard",
5
5
  "bin": {
6
6
  "ccgather": "dist/index.js",