ccclub 0.3.12 → 0.3.14
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 +52 -18
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -56,20 +56,17 @@ var PLAN_LABELS = {
|
|
|
56
56
|
api: "API"
|
|
57
57
|
};
|
|
58
58
|
var MODEL_PRICING = {
|
|
59
|
-
// Claude
|
|
59
|
+
// Claude models. We track API-equivalent value, matching ccusage's calculated mode.
|
|
60
60
|
"claude-opus-4-7": { input: 5, output: 25, cacheCreation: 6.25, cacheRead: 0.5 },
|
|
61
61
|
"claude-opus-4-6": { input: 5, output: 25, cacheCreation: 6.25, cacheRead: 0.5 },
|
|
62
62
|
"claude-opus-4-5": { input: 5, output: 25, cacheCreation: 6.25, cacheRead: 0.5 },
|
|
63
63
|
"claude-opus-4-5-20251101": { input: 5, output: 25, cacheCreation: 6.25, cacheRead: 0.5 },
|
|
64
|
-
// Opus 4.0–4.1
|
|
65
64
|
"claude-opus-4-1-20250805": { input: 15, output: 75, cacheCreation: 18.75, cacheRead: 1.5 },
|
|
66
|
-
// Sonnet
|
|
67
65
|
"claude-sonnet-4-6": { input: 3, output: 15, cacheCreation: 3.75, cacheRead: 0.3 },
|
|
68
66
|
"claude-sonnet-4-5": { input: 3, output: 15, cacheCreation: 3.75, cacheRead: 0.3 },
|
|
69
67
|
"claude-sonnet-4-5-20250929": { input: 3, output: 15, cacheCreation: 3.75, cacheRead: 0.3 },
|
|
70
68
|
"claude-sonnet-4-20250514": { input: 3, output: 15, cacheCreation: 3.75, cacheRead: 0.3 },
|
|
71
69
|
"claude-3-5-sonnet-20241022": { input: 3, output: 15, cacheCreation: 3.75, cacheRead: 0.3 },
|
|
72
|
-
// Haiku
|
|
73
70
|
"claude-haiku-4-5-20251001": { input: 1, output: 5, cacheCreation: 1.25, cacheRead: 0.1 },
|
|
74
71
|
"claude-3-5-haiku-20241022": { input: 0.8, output: 4, cacheCreation: 1, cacheRead: 0.08 },
|
|
75
72
|
// OpenAI GPT/Codex family.
|
|
@@ -79,7 +76,12 @@ var MODEL_PRICING = {
|
|
|
79
76
|
"gpt-5.4-nano": { input: 0.2, output: 1.25, cacheCreation: 0, cacheRead: 0.02 },
|
|
80
77
|
"gpt-5.3-codex": { input: 1.75, output: 14, cacheCreation: 0, cacheRead: 0.175 },
|
|
81
78
|
"gpt-5.2-codex": { input: 1.75, output: 14, cacheCreation: 0, cacheRead: 0.175 },
|
|
79
|
+
"gpt-5.1-codex": { input: 1.25, output: 10, cacheCreation: 0, cacheRead: 0.125 },
|
|
80
|
+
"gpt-5.1-codex-max": { input: 1.25, output: 10, cacheCreation: 0, cacheRead: 0.125 },
|
|
81
|
+
"gpt-5.1-codex-mini": { input: 0.25, output: 2, cacheCreation: 0, cacheRead: 0.025 },
|
|
82
82
|
"gpt-5-codex": { input: 1.25, output: 10, cacheCreation: 0, cacheRead: 0.125 },
|
|
83
|
+
"codex-mini-latest": { input: 1.5, output: 6, cacheCreation: 0, cacheRead: 0.375 },
|
|
84
|
+
"codex-auto-review": { input: 0, output: 0, cacheCreation: 0, cacheRead: 0 },
|
|
83
85
|
"gpt-5": { input: 1.25, output: 10, cacheCreation: 0, cacheRead: 0.125 },
|
|
84
86
|
"gpt-5-mini": { input: 0.25, output: 2, cacheCreation: 0, cacheRead: 0.025 },
|
|
85
87
|
"gpt-5-nano": { input: 0.05, output: 0.4, cacheCreation: 0, cacheRead: 5e-3 }
|
|
@@ -94,7 +96,12 @@ var FAMILY_FALLBACK = {
|
|
|
94
96
|
"gpt-5.4": MODEL_PRICING["gpt-5.4"],
|
|
95
97
|
"gpt-5.3-codex": MODEL_PRICING["gpt-5.3-codex"],
|
|
96
98
|
"gpt-5.2-codex": MODEL_PRICING["gpt-5.2-codex"],
|
|
99
|
+
"gpt-5.1-codex-mini": MODEL_PRICING["gpt-5.1-codex-mini"],
|
|
100
|
+
"gpt-5.1-codex-max": MODEL_PRICING["gpt-5.1-codex-max"],
|
|
101
|
+
"gpt-5.1-codex": MODEL_PRICING["gpt-5.1-codex"],
|
|
97
102
|
"gpt-5-codex": MODEL_PRICING["gpt-5-codex"],
|
|
103
|
+
"codex-mini-latest": MODEL_PRICING["codex-mini-latest"],
|
|
104
|
+
"codex-auto-review": MODEL_PRICING["codex-auto-review"],
|
|
98
105
|
"gpt-5-nano": MODEL_PRICING["gpt-5-nano"],
|
|
99
106
|
"gpt-5-mini": MODEL_PRICING["gpt-5-mini"],
|
|
100
107
|
"gpt-5": MODEL_PRICING["gpt-5"],
|
|
@@ -336,7 +343,7 @@ function isHeartbeatInstalled() {
|
|
|
336
343
|
}
|
|
337
344
|
|
|
338
345
|
// src/commands/sync.ts
|
|
339
|
-
import { readFile as
|
|
346
|
+
import { readFile as readFile5, writeFile as writeFile4 } from "fs/promises";
|
|
340
347
|
import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
|
|
341
348
|
import { join as join11 } from "path";
|
|
342
349
|
import { homedir as homedir11 } from "os";
|
|
@@ -544,7 +551,7 @@ async function collectClaudeUsage() {
|
|
|
544
551
|
const files = await globFiles(projectDirs, "**/*.jsonl");
|
|
545
552
|
const entries = [];
|
|
546
553
|
const turns = [];
|
|
547
|
-
const
|
|
554
|
+
const entryIndexByDedupeKey = /* @__PURE__ */ new Map();
|
|
548
555
|
const seenTurns = /* @__PURE__ */ new Set();
|
|
549
556
|
for (const file of files) {
|
|
550
557
|
await readJsonlFile(file, (value) => {
|
|
@@ -565,7 +572,8 @@ async function collectClaudeUsage() {
|
|
|
565
572
|
const usage = value.message.usage;
|
|
566
573
|
const sessionId = value.sessionId || "";
|
|
567
574
|
const requestId = asString(value.requestId);
|
|
568
|
-
const
|
|
575
|
+
const messageId = asString(value.message.id);
|
|
576
|
+
const dedupeKey = messageId ? requestId ? `${messageId}:${requestId}` : messageId : [
|
|
569
577
|
source,
|
|
570
578
|
sessionId,
|
|
571
579
|
timestamp,
|
|
@@ -574,15 +582,13 @@ async function collectClaudeUsage() {
|
|
|
574
582
|
usage.cache_creation_input_tokens ?? 0,
|
|
575
583
|
usage.cache_read_input_tokens ?? 0
|
|
576
584
|
].join(":");
|
|
577
|
-
if (seen.has(dedupeKey)) return;
|
|
578
|
-
seen.add(dedupeKey);
|
|
579
585
|
const inputTokens = usage.input_tokens || 0;
|
|
580
586
|
const outputTokens = usage.output_tokens || 0;
|
|
581
587
|
const cacheCreationTokens = usage.cache_creation_input_tokens || 0;
|
|
582
588
|
const cacheReadTokens = usage.cache_read_input_tokens || 0;
|
|
583
589
|
const model = value.message.model || "unknown";
|
|
584
590
|
const costUSD = value.costUSD && value.costUSD > 0 ? value.costUSD : calculateCost(model, inputTokens, outputTokens, cacheCreationTokens, cacheReadTokens);
|
|
585
|
-
|
|
591
|
+
const entry = {
|
|
586
592
|
source,
|
|
587
593
|
timestamp,
|
|
588
594
|
sessionId,
|
|
@@ -594,7 +600,16 @@ async function collectClaudeUsage() {
|
|
|
594
600
|
cacheReadTokens,
|
|
595
601
|
totalTokens: inputTokens + outputTokens + cacheCreationTokens + cacheReadTokens,
|
|
596
602
|
costUSD
|
|
597
|
-
}
|
|
603
|
+
};
|
|
604
|
+
const existingIndex = entryIndexByDedupeKey.get(dedupeKey);
|
|
605
|
+
if (existingIndex != null) {
|
|
606
|
+
if (entry.totalTokens > entries[existingIndex].totalTokens) {
|
|
607
|
+
entries[existingIndex] = entry;
|
|
608
|
+
}
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
entryIndexByDedupeKey.set(dedupeKey, entries.length);
|
|
612
|
+
entries.push(entry);
|
|
598
613
|
});
|
|
599
614
|
}
|
|
600
615
|
return { source, entries, turns, files: files.length, warnings: [] };
|
|
@@ -606,12 +621,28 @@ var claudeCollector = {
|
|
|
606
621
|
};
|
|
607
622
|
|
|
608
623
|
// src/sources/codex.ts
|
|
624
|
+
import { readFile as readFile4 } from "fs/promises";
|
|
609
625
|
import { join as join7, relative, sep } from "path";
|
|
610
626
|
import { homedir as homedir7 } from "os";
|
|
627
|
+
var CODEX_FAST_COST_MULTIPLIER = 2;
|
|
628
|
+
var codexFastServiceTierRegex = /(?:^|\n)\s*service_tier\s*=\s*["']?(?:fast|priority)["']?/iu;
|
|
629
|
+
function getCodexHomes() {
|
|
630
|
+
return parsePathList(process.env[CODEX_HOME_ENV], [join7(homedir7(), DEFAULT_CODEX_DIR)]);
|
|
631
|
+
}
|
|
611
632
|
function getCodexSessionDirs() {
|
|
612
|
-
const homes =
|
|
633
|
+
const homes = getCodexHomes();
|
|
613
634
|
return existingDirectories(homes.map((home) => join7(home, "sessions")));
|
|
614
635
|
}
|
|
636
|
+
async function getCodexCostMultiplier() {
|
|
637
|
+
for (const home of getCodexHomes()) {
|
|
638
|
+
try {
|
|
639
|
+
const config = await readFile4(join7(home, "config.toml"), "utf8");
|
|
640
|
+
if (codexFastServiceTierRegex.test(config)) return CODEX_FAST_COST_MULTIPLIER;
|
|
641
|
+
} catch {
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
return 1;
|
|
645
|
+
}
|
|
615
646
|
function normalizeRawUsage(value) {
|
|
616
647
|
const record = asRecord(value);
|
|
617
648
|
if (record == null) return null;
|
|
@@ -619,7 +650,7 @@ function normalizeRawUsage(value) {
|
|
|
619
650
|
const cachedInputTokens = asNumber(record.cached_input_tokens ?? record.cache_read_input_tokens);
|
|
620
651
|
const outputTokens = asNumber(record.output_tokens);
|
|
621
652
|
const reasoningTokens = asNumber(record.reasoning_output_tokens);
|
|
622
|
-
const fallbackTotal = inputTokens + outputTokens;
|
|
653
|
+
const fallbackTotal = inputTokens + outputTokens + reasoningTokens;
|
|
623
654
|
const totalTokens = asNumber(record.total_tokens) || fallbackTotal;
|
|
624
655
|
return { inputTokens, cachedInputTokens, outputTokens, reasoningTokens, totalTokens };
|
|
625
656
|
}
|
|
@@ -643,7 +674,10 @@ function sessionIdForFile(sessionDir, file) {
|
|
|
643
674
|
}
|
|
644
675
|
async function collectCodexUsage() {
|
|
645
676
|
const source = "codex";
|
|
646
|
-
const sessionDirs = await
|
|
677
|
+
const [sessionDirs, costMultiplier] = await Promise.all([
|
|
678
|
+
getCodexSessionDirs(),
|
|
679
|
+
getCodexCostMultiplier()
|
|
680
|
+
]);
|
|
647
681
|
const files = await globFiles(sessionDirs, "**/*.jsonl");
|
|
648
682
|
const entries = [];
|
|
649
683
|
const turns = [];
|
|
@@ -733,7 +767,7 @@ async function collectCodexUsage() {
|
|
|
733
767
|
cacheReadTokens,
|
|
734
768
|
reasoningTokens: 0,
|
|
735
769
|
totalTokens,
|
|
736
|
-
costUSD: calculateCost(model, inputTokens, rawUsage.outputTokens, 0, cacheReadTokens)
|
|
770
|
+
costUSD: calculateCost(model, inputTokens, rawUsage.outputTokens, 0, cacheReadTokens) * costMultiplier
|
|
737
771
|
});
|
|
738
772
|
});
|
|
739
773
|
if (!sawTaskStarted) {
|
|
@@ -1336,12 +1370,12 @@ async function doSync(firstSync = false, silent = false) {
|
|
|
1336
1370
|
const lastSyncPath = getLastSyncPath();
|
|
1337
1371
|
let lastSync = null;
|
|
1338
1372
|
if (existsSync4(lastSyncPath)) {
|
|
1339
|
-
lastSync = (await
|
|
1373
|
+
lastSync = (await readFile5(lastSyncPath, "utf-8")).trim() || null;
|
|
1340
1374
|
}
|
|
1341
1375
|
let lastSyncBySource = {};
|
|
1342
1376
|
if (existsSync4(getLastSyncBySourcePath())) {
|
|
1343
1377
|
try {
|
|
1344
|
-
lastSyncBySource = JSON.parse(await
|
|
1378
|
+
lastSyncBySource = JSON.parse(await readFile5(getLastSyncBySourcePath(), "utf-8"));
|
|
1345
1379
|
} catch {
|
|
1346
1380
|
lastSyncBySource = {};
|
|
1347
1381
|
}
|
|
@@ -2323,7 +2357,7 @@ async function hookCommand() {
|
|
|
2323
2357
|
}
|
|
2324
2358
|
|
|
2325
2359
|
// src/index.ts
|
|
2326
|
-
var VERSION = "0.3.
|
|
2360
|
+
var VERSION = "0.3.14";
|
|
2327
2361
|
startUpdateCheck(VERSION);
|
|
2328
2362
|
var program = new Command();
|
|
2329
2363
|
if (process.argv.slice(2).includes("-v")) {
|