ccclub 0.3.9 → 0.3.11
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 -18
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -241,7 +241,7 @@ function isHookInstalled() {
|
|
|
241
241
|
import { writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
|
|
242
242
|
import { join as join3 } from "path";
|
|
243
243
|
import { homedir as homedir3 } from "os";
|
|
244
|
-
import { existsSync as existsSync3 } from "fs";
|
|
244
|
+
import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
|
|
245
245
|
import { execFile } from "child_process";
|
|
246
246
|
var PLIST_NAME = "dev.ccclub.sync";
|
|
247
247
|
var LAUNCH_AGENTS_DIR = join3(homedir3(), "Library", "LaunchAgents");
|
|
@@ -278,32 +278,49 @@ function getPlist() {
|
|
|
278
278
|
</dict>
|
|
279
279
|
</plist>`;
|
|
280
280
|
}
|
|
281
|
+
function isCurrentPlist() {
|
|
282
|
+
if (!existsSync3(PLIST_PATH)) return false;
|
|
283
|
+
try {
|
|
284
|
+
return readFileSync2(PLIST_PATH, "utf-8") === getPlist();
|
|
285
|
+
} catch {
|
|
286
|
+
return false;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
async function launchctl(args) {
|
|
290
|
+
await new Promise((resolve2, reject) => {
|
|
291
|
+
execFile("launchctl", args, (err) => err ? reject(err) : resolve2());
|
|
292
|
+
});
|
|
293
|
+
}
|
|
281
294
|
async function installHeartbeat() {
|
|
282
295
|
if (process.platform !== "darwin") {
|
|
283
296
|
return false;
|
|
284
297
|
}
|
|
285
|
-
if (
|
|
298
|
+
if (isCurrentPlist()) {
|
|
286
299
|
return true;
|
|
287
300
|
}
|
|
288
301
|
if (!existsSync3(LAUNCH_AGENTS_DIR)) {
|
|
289
302
|
await mkdir3(LAUNCH_AGENTS_DIR, { recursive: true });
|
|
290
303
|
}
|
|
304
|
+
if (existsSync3(PLIST_PATH)) {
|
|
305
|
+
try {
|
|
306
|
+
await launchctl(["unload", PLIST_PATH]);
|
|
307
|
+
} catch {
|
|
308
|
+
}
|
|
309
|
+
}
|
|
291
310
|
await writeFile3(PLIST_PATH, getPlist());
|
|
292
311
|
try {
|
|
293
|
-
await
|
|
294
|
-
execFile("launchctl", ["load", PLIST_PATH], (err) => err ? reject(err) : resolve2());
|
|
295
|
-
});
|
|
312
|
+
await launchctl(["load", PLIST_PATH]);
|
|
296
313
|
} catch {
|
|
297
314
|
}
|
|
298
315
|
return true;
|
|
299
316
|
}
|
|
300
317
|
function isHeartbeatInstalled() {
|
|
301
|
-
return
|
|
318
|
+
return isCurrentPlist();
|
|
302
319
|
}
|
|
303
320
|
|
|
304
321
|
// src/commands/sync.ts
|
|
305
322
|
import { readFile as readFile4, writeFile as writeFile4 } from "fs/promises";
|
|
306
|
-
import { existsSync as existsSync4, readFileSync as
|
|
323
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
|
|
307
324
|
import { join as join11 } from "path";
|
|
308
325
|
import { homedir as homedir11 } from "os";
|
|
309
326
|
import chalk2 from "chalk";
|
|
@@ -614,12 +631,21 @@ async function collectCodexUsage() {
|
|
|
614
631
|
const entries = [];
|
|
615
632
|
const turns = [];
|
|
616
633
|
const seen = /* @__PURE__ */ new Set();
|
|
634
|
+
const seenTurns = /* @__PURE__ */ new Set();
|
|
635
|
+
function addTurn(timestamp, key) {
|
|
636
|
+
if (seenTurns.has(key)) return;
|
|
637
|
+
seenTurns.add(key);
|
|
638
|
+
turns.push({ source, timestamp, key });
|
|
639
|
+
}
|
|
617
640
|
for (const sessionDir of sessionDirs) {
|
|
618
641
|
const sessionFiles = files.filter((file) => file.startsWith(`${sessionDir}${sep}`));
|
|
619
642
|
for (const file of sessionFiles) {
|
|
620
643
|
const sessionId = sessionIdForFile(sessionDir, file);
|
|
621
644
|
let previousTotal = null;
|
|
622
645
|
let currentModel;
|
|
646
|
+
let sawTaskStarted = false;
|
|
647
|
+
const fallbackUserTurns = [];
|
|
648
|
+
const seenFallbackUserTurns = /* @__PURE__ */ new Set();
|
|
623
649
|
await readJsonlFile(file, (value) => {
|
|
624
650
|
const record = asRecord(value);
|
|
625
651
|
if (record == null) return;
|
|
@@ -629,6 +655,25 @@ async function collectCodexUsage() {
|
|
|
629
655
|
currentModel = extractModelFromPayload(payload) ?? currentModel;
|
|
630
656
|
return;
|
|
631
657
|
}
|
|
658
|
+
if (type === "event_msg" && payload?.type === "task_started") {
|
|
659
|
+
const timestamp2 = toIsoTimestamp(payload.started_at ?? record.timestamp);
|
|
660
|
+
if (timestamp2 == null) return;
|
|
661
|
+
const turnId = asString(payload.turn_id);
|
|
662
|
+
const key = turnId != null ? `${source}:${sessionId}:${turnId}` : `${source}:${sessionId}:${timestamp2}:task_started`;
|
|
663
|
+
sawTaskStarted = true;
|
|
664
|
+
addTurn(timestamp2, key);
|
|
665
|
+
return;
|
|
666
|
+
}
|
|
667
|
+
if (type === "event_msg" && payload?.type === "user_message") {
|
|
668
|
+
const timestamp2 = toIsoTimestamp(record.timestamp);
|
|
669
|
+
if (timestamp2 == null) return;
|
|
670
|
+
const key = `${source}:${sessionId}:${timestamp2}:user_message`;
|
|
671
|
+
if (!seenFallbackUserTurns.has(key)) {
|
|
672
|
+
seenFallbackUserTurns.add(key);
|
|
673
|
+
fallbackUserTurns.push({ source, timestamp: timestamp2, key });
|
|
674
|
+
}
|
|
675
|
+
return;
|
|
676
|
+
}
|
|
632
677
|
if (type !== "event_msg" || payload?.type !== "token_count") return;
|
|
633
678
|
const timestamp = toIsoTimestamp(record.timestamp);
|
|
634
679
|
if (timestamp == null) return;
|
|
@@ -673,8 +718,10 @@ async function collectCodexUsage() {
|
|
|
673
718
|
totalTokens,
|
|
674
719
|
costUSD: calculateCost(model, inputTokens, rawUsage.outputTokens, 0, cacheReadTokens)
|
|
675
720
|
});
|
|
676
|
-
turns.push({ source, timestamp, key: dedupeKey });
|
|
677
721
|
});
|
|
722
|
+
if (!sawTaskStarted) {
|
|
723
|
+
for (const turn of fallbackUserTurns) addTurn(turn.timestamp, turn.key);
|
|
724
|
+
}
|
|
678
725
|
}
|
|
679
726
|
}
|
|
680
727
|
return { source, entries, turns, files: files.length, warnings: [] };
|
|
@@ -1106,7 +1153,7 @@ import { execSync as execSync2, exec } from "child_process";
|
|
|
1106
1153
|
import { promisify } from "util";
|
|
1107
1154
|
import { userInfo as userInfo2, homedir as homedir10 } from "os";
|
|
1108
1155
|
import { join as join10 } from "path";
|
|
1109
|
-
import { readFileSync as
|
|
1156
|
+
import { readFileSync as readFileSync3, writeFileSync } from "fs";
|
|
1110
1157
|
var execAsync = promisify(exec);
|
|
1111
1158
|
var debug = (...args) => {
|
|
1112
1159
|
if (process.env.CCCLUB_DEBUG) console.error("[usage-debug]", ...args);
|
|
@@ -1115,7 +1162,7 @@ var CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
|
1115
1162
|
var CACHE_PATH = join10(homedir10(), CCCLUB_CONFIG_DIR, "usage-cache.json");
|
|
1116
1163
|
function readCache(allowStale = false) {
|
|
1117
1164
|
try {
|
|
1118
|
-
const raw =
|
|
1165
|
+
const raw = readFileSync3(CACHE_PATH, "utf-8");
|
|
1119
1166
|
const { snapshot, fetchedAt } = JSON.parse(raw);
|
|
1120
1167
|
if (allowStale || Date.now() - fetchedAt < CACHE_TTL_MS) return snapshot;
|
|
1121
1168
|
} catch {
|
|
@@ -1186,7 +1233,7 @@ async function fetchUsageLimits() {
|
|
|
1186
1233
|
debug("caught error:", err instanceof Error ? err.message : String(err));
|
|
1187
1234
|
}
|
|
1188
1235
|
try {
|
|
1189
|
-
const tmp = JSON.parse(
|
|
1236
|
+
const tmp = JSON.parse(readFileSync3("/tmp/sl-claude-usage", "utf-8"));
|
|
1190
1237
|
if (typeof tmp.fiveHour === "number" && typeof tmp.sevenDay === "number") {
|
|
1191
1238
|
const result = { fiveHour: tmp.fiveHour, sevenDay: tmp.sevenDay, snapshotAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
1192
1239
|
debug("returning cc-costline cache fallback:", result.fiveHour, result.sevenDay);
|
|
@@ -1212,7 +1259,7 @@ function needsFullSync() {
|
|
|
1212
1259
|
const path = getSyncVersionPath();
|
|
1213
1260
|
if (!existsSync4(path)) return true;
|
|
1214
1261
|
try {
|
|
1215
|
-
const stored =
|
|
1262
|
+
const stored = readFileSync4(path, "utf-8").trim();
|
|
1216
1263
|
return stored !== SYNC_FORMAT_VERSION;
|
|
1217
1264
|
} catch {
|
|
1218
1265
|
return true;
|
|
@@ -1224,7 +1271,7 @@ async function syncCommand(options) {
|
|
|
1224
1271
|
if (options.silent && !options.full) {
|
|
1225
1272
|
if (existsSync4(timePath)) {
|
|
1226
1273
|
try {
|
|
1227
|
-
const ts = parseInt(
|
|
1274
|
+
const ts = parseInt(readFileSync4(timePath, "utf-8").trim(), 10);
|
|
1228
1275
|
if (Date.now() - ts < THROTTLE_MS) return;
|
|
1229
1276
|
} catch {
|
|
1230
1277
|
}
|
|
@@ -1569,7 +1616,7 @@ import Table from "cli-table3";
|
|
|
1569
1616
|
import ora4 from "ora";
|
|
1570
1617
|
|
|
1571
1618
|
// src/update-check.ts
|
|
1572
|
-
import { readFileSync as
|
|
1619
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync5 } from "fs";
|
|
1573
1620
|
import { join as join12 } from "path";
|
|
1574
1621
|
import { homedir as homedir12 } from "os";
|
|
1575
1622
|
var CHECK_INTERVAL_MS = 12 * 60 * 60 * 1e3;
|
|
@@ -1579,7 +1626,7 @@ function startUpdateCheck(currentVersion) {
|
|
|
1579
1626
|
if (process.argv.includes("--silent") || process.argv.includes("-s")) return;
|
|
1580
1627
|
try {
|
|
1581
1628
|
if (existsSync5(CHECK_FILE)) {
|
|
1582
|
-
const ts = parseInt(
|
|
1629
|
+
const ts = parseInt(readFileSync5(CHECK_FILE, "utf-8").trim(), 10);
|
|
1583
1630
|
if (Date.now() - ts < CHECK_INTERVAL_MS) return;
|
|
1584
1631
|
}
|
|
1585
1632
|
} catch {
|
|
@@ -1747,7 +1794,7 @@ function printGroup(data, code, period, config, showCache = false, showAll = fal
|
|
|
1747
1794
|
${data.group.name}`));
|
|
1748
1795
|
const periodLabel = { daily: "TODAY", yesterday: "YESTERDAY", weekly: "7 DAYS", monthly: "30 DAYS", "all-time": "ALL TIME" };
|
|
1749
1796
|
const now = Date.now();
|
|
1750
|
-
const activeCount = data.rankings.filter((r) => r
|
|
1797
|
+
const activeCount = data.rankings.filter((r) => isEntryActive(r, now)).length;
|
|
1751
1798
|
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`));
|
|
1752
1799
|
if (activeCount > 0) {
|
|
1753
1800
|
console.log(theme.success(` ${activeCount} active`));
|
|
@@ -1842,7 +1889,10 @@ function printGroup(data, code, period, config, showCache = false, showAll = fal
|
|
|
1842
1889
|
}
|
|
1843
1890
|
}
|
|
1844
1891
|
function isEntryActive(entry, now) {
|
|
1845
|
-
|
|
1892
|
+
const value = entry.lastActiveAt || entry.lastSync;
|
|
1893
|
+
if (!value) return false;
|
|
1894
|
+
const activeAt = new Date(value).getTime();
|
|
1895
|
+
return Number.isFinite(activeAt) && now - activeAt < ACTIVE_THRESHOLD_MS;
|
|
1846
1896
|
}
|
|
1847
1897
|
function podiumStyle(rank) {
|
|
1848
1898
|
if (rank === 1) return theme.gold;
|
|
@@ -2256,7 +2306,7 @@ async function hookCommand() {
|
|
|
2256
2306
|
}
|
|
2257
2307
|
|
|
2258
2308
|
// src/index.ts
|
|
2259
|
-
var VERSION = "0.3.
|
|
2309
|
+
var VERSION = "0.3.11";
|
|
2260
2310
|
startUpdateCheck(VERSION);
|
|
2261
2311
|
var program = new Command();
|
|
2262
2312
|
if (process.argv.slice(2).includes("-v")) {
|