aisessions 1.0.0

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.
@@ -0,0 +1,111 @@
1
+ import { spawnSync } from 'node:child_process'
2
+
3
+ function run(args) {
4
+ const r = spawnSync('npx', ['--yes', 'ccusage@latest', ...args, '--json'], {
5
+ timeout: 30_000, maxBuffer: 16 * 1024 * 1024, encoding: 'utf8',
6
+ env: { ...process.env, NO_COLOR: '1' },
7
+ })
8
+ if (r.status !== 0 || !r.stdout) return null
9
+ try { return JSON.parse(r.stdout) } catch { return null }
10
+ }
11
+
12
+ function runLocal(args) {
13
+ try {
14
+ const r = spawnSync('ccusage', [...args, '--json'], {
15
+ timeout: 30_000, maxBuffer: 16 * 1024 * 1024, encoding: 'utf8',
16
+ env: { ...process.env, NO_COLOR: '1' },
17
+ })
18
+ if (r.status !== 0 || !r.stdout) return null
19
+ return JSON.parse(r.stdout)
20
+ } catch { return null }
21
+ }
22
+
23
+ // Normalise the two very different per-agent response shapes into one schema.
24
+ function normalizeEntries(entries, agentId) {
25
+ return (entries || []).map(d => {
26
+ // Claude-style keys Codex-style keys
27
+ const cost = d.totalCost ?? d.costUSD ?? 0
28
+ const input = d.inputTokens ?? 0
29
+ const output = d.outputTokens ?? 0
30
+ const cacheRead = d.cacheReadTokens ?? d.cachedInputTokens ?? 0
31
+ const cacheWrite= d.cacheCreationTokens ?? 0
32
+ const reasoning = d.reasoningOutputTokens ?? 0
33
+ const total = d.totalTokens ?? (input + output + cacheRead + cacheWrite)
34
+
35
+ let models = []
36
+ if (Array.isArray(d.modelBreakdowns)) {
37
+ // Claude format
38
+ models = d.modelBreakdowns.map(m => ({
39
+ name: m.modelName || '?',
40
+ cost: m.cost ?? 0,
41
+ input: m.inputTokens ?? 0,
42
+ output: m.outputTokens ?? 0,
43
+ cacheRead: m.cacheReadTokens ?? 0,
44
+ cacheWrite: m.cacheCreationTokens ?? 0,
45
+ reasoning: 0,
46
+ }))
47
+ } else if (d.models && typeof d.models === 'object') {
48
+ // Codex format: { "gpt-5.1": { inputTokens, outputTokens, ... } }
49
+ models = Object.entries(d.models).map(([name, m]) => ({
50
+ name,
51
+ cost: m.cost ?? 0,
52
+ input: m.inputTokens ?? 0,
53
+ output: m.outputTokens ?? 0,
54
+ cacheRead: m.cachedInputTokens ?? 0,
55
+ cacheWrite: 0,
56
+ reasoning: m.reasoningOutputTokens ?? 0,
57
+ }))
58
+ } else if (Array.isArray(d.modelsUsed)) {
59
+ models = d.modelsUsed.map(name => ({ name, cost: 0, input: 0, output: 0, cacheRead: 0, cacheWrite: 0, reasoning: 0 }))
60
+ }
61
+
62
+ return {
63
+ date: d.date || d.period || '',
64
+ agent: agentId || 'all',
65
+ cost, input, output, cacheRead, cacheWrite, reasoning, total,
66
+ models,
67
+ }
68
+ })
69
+ }
70
+
71
+ function totals(days) {
72
+ return days.reduce((a, d) => ({
73
+ totalCost: a.totalCost + d.cost,
74
+ inputTokens: a.inputTokens + d.input,
75
+ outputTokens: a.outputTokens + d.output,
76
+ cacheReadTokens: a.cacheReadTokens + d.cacheRead,
77
+ cacheCreationTokens: a.cacheCreationTokens + d.cacheWrite,
78
+ reasoningTokens: a.reasoningTokens + d.reasoning,
79
+ totalTokens: a.totalTokens + d.total,
80
+ }), { totalCost: 0, inputTokens: 0, outputTokens: 0, cacheReadTokens: 0, cacheCreationTokens: 0, reasoningTokens: 0, totalTokens: 0 })
81
+ }
82
+
83
+ export async function getUsageData({ since, until, agent } = {}) {
84
+ const dateArgs = []
85
+ if (since) dateArgs.push('--since', since)
86
+ if (until) dateArgs.push('--until', until)
87
+
88
+ let raw
89
+ if (agent && agent !== 'all') {
90
+ // Per-agent command: npx ccusage claude daily --json
91
+ const agentArgs = [agent, 'daily', ...dateArgs]
92
+ raw = runLocal(agentArgs) ?? run(agentArgs)
93
+ if (!raw) {
94
+ // Fallback: run global and filter by metadata.agents
95
+ const globalArgs = ['daily', ...dateArgs]
96
+ const g = runLocal(globalArgs) ?? run(globalArgs)
97
+ if (g && Array.isArray(g.daily)) {
98
+ raw = { daily: g.daily.filter(d => (d.metadata?.agents || []).includes(agent)) }
99
+ }
100
+ }
101
+ } else {
102
+ raw = runLocal(['daily', ...dateArgs]) ?? run(['daily', ...dateArgs])
103
+ }
104
+
105
+ if (!raw) return { available: false, error: 'ccusage not found — run: npm i -g ccusage' }
106
+
107
+ const entries = normalizeEntries(raw.daily || (Array.isArray(raw) ? raw : []), agent || 'all')
108
+ entries.sort((a, b) => (a.date < b.date ? -1 : a.date > b.date ? 1 : 0))
109
+
110
+ return { available: true, agent: agent || 'all', daily: entries, totals: totals(entries) }
111
+ }