ccclub 0.3.10 → 0.3.12

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 +57 -20
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -56,20 +56,30 @@ var PLAN_LABELS = {
56
56
  api: "API"
57
57
  };
58
58
  var MODEL_PRICING = {
59
- // Opus 4.5+
59
+ // Claude Opus 4.5+
60
+ "claude-opus-4-7": { input: 5, output: 25, cacheCreation: 6.25, cacheRead: 0.5 },
60
61
  "claude-opus-4-6": { input: 5, output: 25, cacheCreation: 6.25, cacheRead: 0.5 },
62
+ "claude-opus-4-5": { input: 5, output: 25, cacheCreation: 6.25, cacheRead: 0.5 },
61
63
  "claude-opus-4-5-20251101": { input: 5, output: 25, cacheCreation: 6.25, cacheRead: 0.5 },
62
64
  // Opus 4.0–4.1
63
65
  "claude-opus-4-1-20250805": { input: 15, output: 75, cacheCreation: 18.75, cacheRead: 1.5 },
64
66
  // Sonnet
67
+ "claude-sonnet-4-6": { input: 3, output: 15, cacheCreation: 3.75, cacheRead: 0.3 },
68
+ "claude-sonnet-4-5": { input: 3, output: 15, cacheCreation: 3.75, cacheRead: 0.3 },
65
69
  "claude-sonnet-4-5-20250929": { input: 3, output: 15, cacheCreation: 3.75, cacheRead: 0.3 },
66
70
  "claude-sonnet-4-20250514": { input: 3, output: 15, cacheCreation: 3.75, cacheRead: 0.3 },
67
71
  "claude-3-5-sonnet-20241022": { input: 3, output: 15, cacheCreation: 3.75, cacheRead: 0.3 },
68
72
  // Haiku
69
73
  "claude-haiku-4-5-20251001": { input: 1, output: 5, cacheCreation: 1.25, cacheRead: 0.1 },
70
74
  "claude-3-5-haiku-20241022": { input: 0.8, output: 4, cacheCreation: 1, cacheRead: 0.08 },
71
- // OpenAI GPT family fallbacks. Many agent logs provide exact costs; these are best-effort
72
- // estimates for sources that only expose tokens.
75
+ // OpenAI GPT/Codex family.
76
+ "gpt-5.5": { input: 5, output: 30, cacheCreation: 0, cacheRead: 0.5 },
77
+ "gpt-5.4": { input: 2.5, output: 15, cacheCreation: 0, cacheRead: 0.25 },
78
+ "gpt-5.4-mini": { input: 0.75, output: 4.5, cacheCreation: 0, cacheRead: 0.075 },
79
+ "gpt-5.4-nano": { input: 0.2, output: 1.25, cacheCreation: 0, cacheRead: 0.02 },
80
+ "gpt-5.3-codex": { input: 1.75, output: 14, cacheCreation: 0, cacheRead: 0.175 },
81
+ "gpt-5.2-codex": { input: 1.75, output: 14, cacheCreation: 0, cacheRead: 0.175 },
82
+ "gpt-5-codex": { input: 1.25, output: 10, cacheCreation: 0, cacheRead: 0.125 },
73
83
  "gpt-5": { input: 1.25, output: 10, cacheCreation: 0, cacheRead: 0.125 },
74
84
  "gpt-5-mini": { input: 0.25, output: 2, cacheCreation: 0, cacheRead: 0.025 },
75
85
  "gpt-5-nano": { input: 0.05, output: 0.4, cacheCreation: 0, cacheRead: 5e-3 }
@@ -78,6 +88,13 @@ var FAMILY_FALLBACK = {
78
88
  opus: MODEL_PRICING["claude-opus-4-6"],
79
89
  sonnet: MODEL_PRICING["claude-sonnet-4-5-20250929"],
80
90
  haiku: MODEL_PRICING["claude-haiku-4-5-20251001"],
91
+ "gpt-5.5": MODEL_PRICING["gpt-5.5"],
92
+ "gpt-5.4-mini": MODEL_PRICING["gpt-5.4-mini"],
93
+ "gpt-5.4-nano": MODEL_PRICING["gpt-5.4-nano"],
94
+ "gpt-5.4": MODEL_PRICING["gpt-5.4"],
95
+ "gpt-5.3-codex": MODEL_PRICING["gpt-5.3-codex"],
96
+ "gpt-5.2-codex": MODEL_PRICING["gpt-5.2-codex"],
97
+ "gpt-5-codex": MODEL_PRICING["gpt-5-codex"],
81
98
  "gpt-5-nano": MODEL_PRICING["gpt-5-nano"],
82
99
  "gpt-5-mini": MODEL_PRICING["gpt-5-mini"],
83
100
  "gpt-5": MODEL_PRICING["gpt-5"],
@@ -241,7 +258,7 @@ function isHookInstalled() {
241
258
  import { writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
242
259
  import { join as join3 } from "path";
243
260
  import { homedir as homedir3 } from "os";
244
- import { existsSync as existsSync3 } from "fs";
261
+ import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
245
262
  import { execFile } from "child_process";
246
263
  var PLIST_NAME = "dev.ccclub.sync";
247
264
  var LAUNCH_AGENTS_DIR = join3(homedir3(), "Library", "LaunchAgents");
@@ -278,32 +295,49 @@ function getPlist() {
278
295
  </dict>
279
296
  </plist>`;
280
297
  }
298
+ function isCurrentPlist() {
299
+ if (!existsSync3(PLIST_PATH)) return false;
300
+ try {
301
+ return readFileSync2(PLIST_PATH, "utf-8") === getPlist();
302
+ } catch {
303
+ return false;
304
+ }
305
+ }
306
+ async function launchctl(args) {
307
+ await new Promise((resolve2, reject) => {
308
+ execFile("launchctl", args, (err) => err ? reject(err) : resolve2());
309
+ });
310
+ }
281
311
  async function installHeartbeat() {
282
312
  if (process.platform !== "darwin") {
283
313
  return false;
284
314
  }
285
- if (existsSync3(PLIST_PATH)) {
315
+ if (isCurrentPlist()) {
286
316
  return true;
287
317
  }
288
318
  if (!existsSync3(LAUNCH_AGENTS_DIR)) {
289
319
  await mkdir3(LAUNCH_AGENTS_DIR, { recursive: true });
290
320
  }
321
+ if (existsSync3(PLIST_PATH)) {
322
+ try {
323
+ await launchctl(["unload", PLIST_PATH]);
324
+ } catch {
325
+ }
326
+ }
291
327
  await writeFile3(PLIST_PATH, getPlist());
292
328
  try {
293
- await new Promise((resolve2, reject) => {
294
- execFile("launchctl", ["load", PLIST_PATH], (err) => err ? reject(err) : resolve2());
295
- });
329
+ await launchctl(["load", PLIST_PATH]);
296
330
  } catch {
297
331
  }
298
332
  return true;
299
333
  }
300
334
  function isHeartbeatInstalled() {
301
- return existsSync3(PLIST_PATH);
335
+ return isCurrentPlist();
302
336
  }
303
337
 
304
338
  // src/commands/sync.ts
305
339
  import { readFile as readFile4, writeFile as writeFile4 } from "fs/promises";
306
- import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
340
+ import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
307
341
  import { join as join11 } from "path";
308
342
  import { homedir as homedir11 } from "os";
309
343
  import chalk2 from "chalk";
@@ -1136,7 +1170,7 @@ import { execSync as execSync2, exec } from "child_process";
1136
1170
  import { promisify } from "util";
1137
1171
  import { userInfo as userInfo2, homedir as homedir10 } from "os";
1138
1172
  import { join as join10 } from "path";
1139
- import { readFileSync as readFileSync2, writeFileSync } from "fs";
1173
+ import { readFileSync as readFileSync3, writeFileSync } from "fs";
1140
1174
  var execAsync = promisify(exec);
1141
1175
  var debug = (...args) => {
1142
1176
  if (process.env.CCCLUB_DEBUG) console.error("[usage-debug]", ...args);
@@ -1145,7 +1179,7 @@ var CACHE_TTL_MS = 5 * 60 * 1e3;
1145
1179
  var CACHE_PATH = join10(homedir10(), CCCLUB_CONFIG_DIR, "usage-cache.json");
1146
1180
  function readCache(allowStale = false) {
1147
1181
  try {
1148
- const raw = readFileSync2(CACHE_PATH, "utf-8");
1182
+ const raw = readFileSync3(CACHE_PATH, "utf-8");
1149
1183
  const { snapshot, fetchedAt } = JSON.parse(raw);
1150
1184
  if (allowStale || Date.now() - fetchedAt < CACHE_TTL_MS) return snapshot;
1151
1185
  } catch {
@@ -1216,7 +1250,7 @@ async function fetchUsageLimits() {
1216
1250
  debug("caught error:", err instanceof Error ? err.message : String(err));
1217
1251
  }
1218
1252
  try {
1219
- const tmp = JSON.parse(readFileSync2("/tmp/sl-claude-usage", "utf-8"));
1253
+ const tmp = JSON.parse(readFileSync3("/tmp/sl-claude-usage", "utf-8"));
1220
1254
  if (typeof tmp.fiveHour === "number" && typeof tmp.sevenDay === "number") {
1221
1255
  const result = { fiveHour: tmp.fiveHour, sevenDay: tmp.sevenDay, snapshotAt: (/* @__PURE__ */ new Date()).toISOString() };
1222
1256
  debug("returning cc-costline cache fallback:", result.fiveHour, result.sevenDay);
@@ -1242,7 +1276,7 @@ function needsFullSync() {
1242
1276
  const path = getSyncVersionPath();
1243
1277
  if (!existsSync4(path)) return true;
1244
1278
  try {
1245
- const stored = readFileSync3(path, "utf-8").trim();
1279
+ const stored = readFileSync4(path, "utf-8").trim();
1246
1280
  return stored !== SYNC_FORMAT_VERSION;
1247
1281
  } catch {
1248
1282
  return true;
@@ -1254,7 +1288,7 @@ async function syncCommand(options) {
1254
1288
  if (options.silent && !options.full) {
1255
1289
  if (existsSync4(timePath)) {
1256
1290
  try {
1257
- const ts = parseInt(readFileSync3(timePath, "utf-8").trim(), 10);
1291
+ const ts = parseInt(readFileSync4(timePath, "utf-8").trim(), 10);
1258
1292
  if (Date.now() - ts < THROTTLE_MS) return;
1259
1293
  } catch {
1260
1294
  }
@@ -1599,7 +1633,7 @@ import Table from "cli-table3";
1599
1633
  import ora4 from "ora";
1600
1634
 
1601
1635
  // src/update-check.ts
1602
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync5 } from "fs";
1636
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync5 } from "fs";
1603
1637
  import { join as join12 } from "path";
1604
1638
  import { homedir as homedir12 } from "os";
1605
1639
  var CHECK_INTERVAL_MS = 12 * 60 * 60 * 1e3;
@@ -1609,7 +1643,7 @@ function startUpdateCheck(currentVersion) {
1609
1643
  if (process.argv.includes("--silent") || process.argv.includes("-s")) return;
1610
1644
  try {
1611
1645
  if (existsSync5(CHECK_FILE)) {
1612
- const ts = parseInt(readFileSync4(CHECK_FILE, "utf-8").trim(), 10);
1646
+ const ts = parseInt(readFileSync5(CHECK_FILE, "utf-8").trim(), 10);
1613
1647
  if (Date.now() - ts < CHECK_INTERVAL_MS) return;
1614
1648
  }
1615
1649
  } catch {
@@ -1777,7 +1811,7 @@ function printGroup(data, code, period, config, showCache = false, showAll = fal
1777
1811
  ${data.group.name}`));
1778
1812
  const periodLabel = { daily: "TODAY", yesterday: "YESTERDAY", weekly: "7 DAYS", monthly: "30 DAYS", "all-time": "ALL TIME" };
1779
1813
  const now = Date.now();
1780
- const activeCount = data.rankings.filter((r) => r.lastSync && now - new Date(r.lastSync).getTime() < ACTIVE_THRESHOLD_MS).length;
1814
+ const activeCount = data.rankings.filter((r) => isEntryActive(r, now)).length;
1781
1815
  console.log(theme.muted(` ${periodLabel[period] || period.toUpperCase()} \xB7 ${data.start.slice(0, 10)} \u2192 ${data.end.slice(0, 10)} \xB7 ${data.group.memberCount} members`));
1782
1816
  if (activeCount > 0) {
1783
1817
  console.log(theme.success(` ${activeCount} active`));
@@ -1872,7 +1906,10 @@ function printGroup(data, code, period, config, showCache = false, showAll = fal
1872
1906
  }
1873
1907
  }
1874
1908
  function isEntryActive(entry, now) {
1875
- return Boolean(entry.lastSync && now - new Date(entry.lastSync).getTime() < ACTIVE_THRESHOLD_MS);
1909
+ const value = entry.lastActiveAt || entry.lastSync;
1910
+ if (!value) return false;
1911
+ const activeAt = new Date(value).getTime();
1912
+ return Number.isFinite(activeAt) && now - activeAt < ACTIVE_THRESHOLD_MS;
1876
1913
  }
1877
1914
  function podiumStyle(rank) {
1878
1915
  if (rank === 1) return theme.gold;
@@ -2286,7 +2323,7 @@ async function hookCommand() {
2286
2323
  }
2287
2324
 
2288
2325
  // src/index.ts
2289
- var VERSION = "0.3.10";
2326
+ var VERSION = "0.3.12";
2290
2327
  startUpdateCheck(VERSION);
2291
2328
  var program = new Command();
2292
2329
  if (process.argv.slice(2).includes("-v")) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccclub",
3
- "version": "0.3.10",
3
+ "version": "0.3.12",
4
4
  "type": "module",
5
5
  "description": "Claude Code and Codex leaderboard among friends for coding agent tokens and costs",
6
6
  "bin": {