clanker-stats 0.3.2 → 0.4.1
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/cli.js +35 -22
- package/package.json +1 -1
package/cli.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { readFile, writeFile } from "node:fs/promises"
|
|
4
|
+
import { createReadStream } from "node:fs"
|
|
5
|
+
import { createInterface } from "node:readline"
|
|
4
6
|
import { homedir } from "node:os"
|
|
5
7
|
import { join } from "node:path"
|
|
6
8
|
import { execFileSync } from "node:child_process"
|
|
@@ -41,31 +43,39 @@ async function collectClaude() {
|
|
|
41
43
|
async function collectCodex() {
|
|
42
44
|
const counts = new Map()
|
|
43
45
|
const dir = join(home, ".codex", "sessions")
|
|
44
|
-
for (const path of await fg("
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
46
|
+
for (const path of await fg("**/*.jsonl", { cwd: dir })) {
|
|
47
|
+
try {
|
|
48
|
+
const rl = createInterface({ input: createReadStream(join(dir, path)), crlfDelay: Infinity })
|
|
49
|
+
let prevCumulative = null
|
|
50
|
+
for await (const line of rl) {
|
|
51
|
+
if (!line.includes('"token_count"')) continue
|
|
52
|
+
try {
|
|
53
|
+
const obj = JSON.parse(line)
|
|
54
|
+
if (obj.payload?.type !== "token_count") continue
|
|
55
|
+
const cumTotal = obj.payload.info?.total_token_usage?.total_tokens
|
|
56
|
+
if (cumTotal != null && cumTotal === prevCumulative) continue
|
|
57
|
+
let tokens = obj.payload.info?.last_token_usage?.total_tokens
|
|
58
|
+
if (!tokens && cumTotal != null && prevCumulative != null) tokens = cumTotal - prevCumulative
|
|
59
|
+
if (cumTotal != null) prevCumulative = cumTotal
|
|
60
|
+
if (tokens > 0 && obj.timestamp) add(counts, isoToDate(obj.timestamp), tokens)
|
|
61
|
+
} catch {}
|
|
62
|
+
}
|
|
63
|
+
} catch {}
|
|
59
64
|
}
|
|
60
65
|
return counts
|
|
61
66
|
}
|
|
62
67
|
|
|
63
68
|
async function collectOpenCode() {
|
|
64
69
|
const counts = new Map()
|
|
65
|
-
const
|
|
66
|
-
|
|
70
|
+
const dirs = [
|
|
71
|
+
join(home, ".local", "share", "opencode", "storage", "message"),
|
|
72
|
+
...(process.platform === "darwin" ? [join(home, "Library", "Application Support", "opencode", "storage", "message")] : []),
|
|
73
|
+
]
|
|
74
|
+
for (const dir of dirs)
|
|
75
|
+
for (const path of await fg("ses_*/msg_*.json", { cwd: dir, suppressErrors: true })) {
|
|
67
76
|
try {
|
|
68
77
|
const obj = JSON.parse(await readFile(join(dir, path), "utf-8"))
|
|
78
|
+
if (obj.role !== "assistant") continue
|
|
69
79
|
const t = obj.tokens
|
|
70
80
|
if (!t) continue
|
|
71
81
|
const tokens = (t.input || 0) + (t.output || 0) + (t.reasoning || 0) +
|
|
@@ -79,12 +89,15 @@ async function collectOpenCode() {
|
|
|
79
89
|
async function collectGemini() {
|
|
80
90
|
const counts = new Map()
|
|
81
91
|
const dir = join(home, ".gemini", "tmp")
|
|
82
|
-
for (const path of await fg("*/chats
|
|
92
|
+
for (const path of await fg("*/chats/*.json", { cwd: dir })) {
|
|
83
93
|
try {
|
|
84
94
|
const obj = JSON.parse(await readFile(join(dir, path), "utf-8"))
|
|
85
95
|
if (!obj.messages) continue
|
|
86
96
|
for (const msg of obj.messages) {
|
|
87
|
-
if (msg.
|
|
97
|
+
if (msg.type !== "gemini") continue
|
|
98
|
+
const t = msg.tokens
|
|
99
|
+
const tk = t?.total || ((t?.input || 0) + (t?.tool || 0) + (t?.output || 0) + (t?.thoughts || 0) + (t?.cached || 0))
|
|
100
|
+
if (tk > 0 && msg.timestamp) add(counts, isoToDate(msg.timestamp), tk)
|
|
88
101
|
}
|
|
89
102
|
} catch {}
|
|
90
103
|
}
|
|
@@ -114,14 +127,15 @@ async function collectAmp() {
|
|
|
114
127
|
async function collectPi() {
|
|
115
128
|
const counts = new Map()
|
|
116
129
|
const dir = join(home, ".pi", "agent", "sessions")
|
|
117
|
-
for (const path of await fg("
|
|
130
|
+
for (const path of await fg("**/*.jsonl", { cwd: dir })) {
|
|
118
131
|
const text = await readFile(join(dir, path), "utf-8")
|
|
119
132
|
for (const line of text.split("\n")) {
|
|
120
133
|
if (!line.includes('"usage"')) continue
|
|
121
134
|
try {
|
|
122
135
|
const obj = JSON.parse(line)
|
|
123
136
|
if (obj.type !== "message") continue
|
|
124
|
-
const
|
|
137
|
+
const u = obj.message?.usage || obj.usage
|
|
138
|
+
const tokens = u?.totalTokens || ((u?.input || 0) + (u?.output || 0) + (u?.reasoning || 0) + (u?.cacheRead || 0) + (u?.cacheWrite || 0))
|
|
125
139
|
if (tokens > 0 && obj.timestamp) add(counts, isoToDate(obj.timestamp), tokens)
|
|
126
140
|
} catch {}
|
|
127
141
|
}
|
|
@@ -290,7 +304,6 @@ function openPath(target) {
|
|
|
290
304
|
async function main() {
|
|
291
305
|
const share = process.argv.includes("--share")
|
|
292
306
|
const results = []
|
|
293
|
-
|
|
294
307
|
for (const tool of tools) {
|
|
295
308
|
try {
|
|
296
309
|
const counts = await tool.collect()
|