claude-roi 0.3.4 → 0.5.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.
- package/README.md +3 -3
- package/package.json +1 -1
- package/src/dashboard.html +17 -2
- package/src/metrics.js +17 -1
package/README.md
CHANGED
|
@@ -116,11 +116,11 @@ Parsed session data is cached at `~/.cache/claude-roi/parsed-sessions.json`. On
|
|
|
116
116
|
|
|
117
117
|
### Cost Calculation
|
|
118
118
|
|
|
119
|
-
Token costs are version-aware and calculated per model:
|
|
119
|
+
Token costs are version-aware and calculated per model (see [Anthropic pricing](https://platform.claude.com/docs/en/about-claude/pricing)):
|
|
120
120
|
|
|
121
121
|
| Model | Input | Output | Cache Read | Cache Write |
|
|
122
122
|
| --- | --- | --- | --- | --- |
|
|
123
|
-
| Opus 4.6 | $
|
|
123
|
+
| Opus 4.6 | $5/M | $25/M | $0.50/M | $6.25/M |
|
|
124
124
|
| Opus 4.5 | $5/M | $25/M | $0.50/M | $6.25/M |
|
|
125
125
|
| Opus 4.0/4.1 (legacy) | $15/M | $75/M | $1.50/M | $18.75/M |
|
|
126
126
|
| Sonnet 3.7/4.0/4.5/4.6 | $3/M | $15/M | $0.30/M | $3.75/M |
|
|
@@ -161,7 +161,7 @@ git push --follow-tags
|
|
|
161
161
|
|
|
162
162
|
This automatically publishes to npm and creates a GitHub Release with auto-generated notes.
|
|
163
163
|
|
|
164
|
-
**Setup (one-time):**
|
|
164
|
+
**Setup (one-time):** Configure [trusted publishing](https://docs.npmjs.com/trusted-publishers/) on npm for the `claude-roi` package, linking it to the GitHub Actions workflow. No tokens or secrets needed.
|
|
165
165
|
|
|
166
166
|
## Contributing
|
|
167
167
|
|
package/package.json
CHANGED
package/src/dashboard.html
CHANGED
|
@@ -1352,6 +1352,7 @@
|
|
|
1352
1352
|
<div class="meta-info">
|
|
1353
1353
|
<span class="badge" id="date-range"></span>
|
|
1354
1354
|
<span class="badge">All data stays local</span>
|
|
1355
|
+
<span class="badge">No tracking, no telemetry</span>
|
|
1355
1356
|
</div>
|
|
1356
1357
|
</header>
|
|
1357
1358
|
|
|
@@ -1516,7 +1517,11 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|
|
1516
1517
|
|
|
1517
1518
|
function render() {
|
|
1518
1519
|
const d = DATA;
|
|
1519
|
-
|
|
1520
|
+
const fmtDate = iso => new Date(iso).toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' });
|
|
1521
|
+
const dateLabel = (d.meta.startDate && d.meta.endDate)
|
|
1522
|
+
? `${fmtDate(d.meta.startDate)} – ${fmtDate(d.meta.endDate)}`
|
|
1523
|
+
: `Last ${d.meta.daysAnalyzed} days`;
|
|
1524
|
+
document.getElementById('date-range').textContent = dateLabel;
|
|
1520
1525
|
const summary = d.summary;
|
|
1521
1526
|
const t = d.tokenAnalytics;
|
|
1522
1527
|
|
|
@@ -2187,7 +2192,17 @@ function initCharts() {
|
|
|
2187
2192
|
const model = modelLabels[ctx.dataIndex];
|
|
2188
2193
|
const d = models[model];
|
|
2189
2194
|
const tpc = d.tokensPerCommit ? formatTokens(d.tokensPerCommit) + ' tok/commit' : 'no commits';
|
|
2190
|
-
|
|
2195
|
+
const lines = [
|
|
2196
|
+
` $${d.cost.toFixed(2)} | ${formatTokens(d.tokens)} tokens | ${d.sessions} sessions | ${Math.round(d.commits)} commits | ${tpc}`,
|
|
2197
|
+
];
|
|
2198
|
+
const subEntries = Object.entries(d.subModels || {});
|
|
2199
|
+
if (subEntries.length >= 1) {
|
|
2200
|
+
for (const [modelId, sub] of subEntries) {
|
|
2201
|
+
const pct = d.cost > 0 ? Math.round((sub.cost / d.cost) * 100) : 0;
|
|
2202
|
+
lines.push(` ${formatModelName(modelId)}: $${sub.cost.toFixed(2)} | ${formatTokens(sub.tokens)} tokens (${pct}%)`);
|
|
2203
|
+
}
|
|
2204
|
+
}
|
|
2205
|
+
return lines;
|
|
2191
2206
|
},
|
|
2192
2207
|
},
|
|
2193
2208
|
},
|
package/src/metrics.js
CHANGED
|
@@ -420,11 +420,18 @@ export function computeMetrics(correlatedSessions, organicCommits, commitsByRepo
|
|
|
420
420
|
for (const [model, data] of Object.entries(session.modelBreakdown)) {
|
|
421
421
|
const family = getModelFamily(model) || 'unknown';
|
|
422
422
|
if (!modelBreakdown[family]) {
|
|
423
|
-
modelBreakdown[family] = { cost: 0, tokens: 0, sessions: 0, commits: 0, avgCostPerCommit: null };
|
|
423
|
+
modelBreakdown[family] = { cost: 0, tokens: 0, sessions: 0, commits: 0, avgCostPerCommit: null, subModels: {} };
|
|
424
424
|
}
|
|
425
425
|
modelBreakdown[family].cost += data.cost;
|
|
426
426
|
modelBreakdown[family].tokens += data.tokens;
|
|
427
427
|
|
|
428
|
+
// Accumulate sub-model cost and tokens within this family
|
|
429
|
+
if (!modelBreakdown[family].subModels[model]) {
|
|
430
|
+
modelBreakdown[family].subModels[model] = { cost: 0, tokens: 0 };
|
|
431
|
+
}
|
|
432
|
+
modelBreakdown[family].subModels[model].cost += data.cost;
|
|
433
|
+
modelBreakdown[family].subModels[model].tokens += data.tokens;
|
|
434
|
+
|
|
428
435
|
// Distribute sessions and commits proportionally by token share
|
|
429
436
|
const share = sessionTotalTokens > 0 ? data.tokens / sessionTotalTokens : 0;
|
|
430
437
|
modelBreakdown[family].sessions += share;
|
|
@@ -435,6 +442,9 @@ export function computeMetrics(correlatedSessions, organicCommits, commitsByRepo
|
|
|
435
442
|
data.sessions = Math.round(data.sessions);
|
|
436
443
|
data.avgCostPerCommit = data.commits > 0 ? data.cost / data.commits : null;
|
|
437
444
|
data.tokensPerCommit = data.commits > 0 ? Math.round(data.tokens / data.commits) : null;
|
|
445
|
+
data.subModels = Object.fromEntries(
|
|
446
|
+
Object.entries(data.subModels).sort(([, a], [, b]) => b.cost - a.cost)
|
|
447
|
+
);
|
|
438
448
|
}
|
|
439
449
|
|
|
440
450
|
// ---- Tool breakdown ----
|
|
@@ -563,6 +573,12 @@ export function computeMetrics(correlatedSessions, organicCommits, commitsByRepo
|
|
|
563
573
|
meta: {
|
|
564
574
|
generatedAt: new Date().toISOString(),
|
|
565
575
|
daysAnalyzed: days,
|
|
576
|
+
startDate: correlatedSessions.length > 0
|
|
577
|
+
? new Date(Math.min(...correlatedSessions.map(s => new Date(s.startTime).getTime()))).toISOString()
|
|
578
|
+
: null,
|
|
579
|
+
endDate: correlatedSessions.length > 0
|
|
580
|
+
? new Date(Math.max(...correlatedSessions.map(s => new Date(s.startTime).getTime()))).toISOString()
|
|
581
|
+
: null,
|
|
566
582
|
defaultBranches: Object.fromEntries(
|
|
567
583
|
Object.entries(commitsByRepo).map(([repo, a]) => [repo.split('/').pop(), a.defaultBranch]).filter(([, b]) => b)
|
|
568
584
|
),
|