clawculator 2.3.0 → 2.3.2
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/package.json
CHANGED
|
@@ -690,7 +690,7 @@ function analyzeSessions(sessionsPath) {
|
|
|
690
690
|
|
|
691
691
|
const ageMs = updatedAt ? Date.now() - new Date(updatedAt).getTime() : null;
|
|
692
692
|
const ageDays = ageMs ? ageMs / (1000 * 3600 * 24) : null;
|
|
693
|
-
const dailyCost = (ageDays && ageDays > 0
|
|
693
|
+
const dailyCost = (ageDays && ageDays > 1.0 && realCost > 0) ? realCost / ageDays : null;
|
|
694
694
|
|
|
695
695
|
const realModelKey = resolveModel(realModel);
|
|
696
696
|
breakdown.push({
|
|
@@ -69,7 +69,8 @@ async function generateHTMLReport(analysis, outPath) {
|
|
|
69
69
|
</div>
|
|
70
70
|
<div style="font-weight:600; color:#111; margin-bottom:4px">${f.message}</div>
|
|
71
71
|
${f.detail ? `<div style="color:#555; font-size:14px; margin-bottom:6px; white-space:pre-line">${f.detail}</div>` : ''}
|
|
72
|
-
${f.
|
|
72
|
+
${f.fix ? `<div style="color:#16a34a; font-size:14px; margin-top:8px;">→ ${f.fix}</div>` : ''}
|
|
73
|
+
${f.command ? `<div style="background:#0f172a; color:#38bdf8; font-family:monospace; font-size:13px; padding:10px 14px; border-radius:6px; margin-top:6px; white-space:pre-wrap; border:1px solid #1e293b;"><span style="color:#6b7280; user-select:none;">$ </span>${f.command}</div>` : ''}
|
|
73
74
|
</div>
|
|
74
75
|
`).join('');
|
|
75
76
|
|
|
@@ -243,7 +244,60 @@ async function generateHTMLReport(analysis, outPath) {
|
|
|
243
244
|
${untrackedHidden > 0 ? `<div style="margin-top:8px; font-size:12px; color:#64748b;">+ ${untrackedHidden} more not shown</div>` : ''}
|
|
244
245
|
</div>` : ''}
|
|
245
246
|
|
|
246
|
-
${burnSummary}
|
|
247
|
+
${burnSummary}
|
|
248
|
+
|
|
249
|
+
<div class="section">
|
|
250
|
+
<div class="section-title">Summary</div>
|
|
251
|
+
<div style="display:grid; grid-template-columns:repeat(auto-fit, minmax(200px, 1fr)); gap:16px;">
|
|
252
|
+
<div>
|
|
253
|
+
<div style="font-size:13px; color:#94a3b8;">Severity Breakdown</div>
|
|
254
|
+
<div style="margin-top:4px;">🔴 ${summary.critical} critical · 🟠 ${summary.high} high · 🟡 ${summary.medium} medium · 🔵 ${summary.low||0} low · ✅ ${summary.info} ok</div>
|
|
255
|
+
</div>
|
|
256
|
+
<div>
|
|
257
|
+
<div style="font-size:13px; color:#94a3b8;">Sessions Analyzed</div>
|
|
258
|
+
<div style="margin-top:4px; font-size:18px; font-weight:700;">${summary.sessionsAnalyzed}</div>
|
|
259
|
+
</div>
|
|
260
|
+
<div>
|
|
261
|
+
<div style="font-size:13px; color:#94a3b8;">Total Tokens</div>
|
|
262
|
+
<div style="margin-top:4px; font-size:18px; font-weight:700;">${(summary.totalTokensFound||0).toLocaleString()}</div>
|
|
263
|
+
</div>
|
|
264
|
+
${summary.todayCost > 0 ? `<div>
|
|
265
|
+
<div style="font-size:13px; color:#94a3b8;">Today's Spend</div>
|
|
266
|
+
<div style="margin-top:4px; font-size:18px; font-weight:700; color:#ef4444;">$${summary.todayCost.toFixed(2)}</div>
|
|
267
|
+
</div>` : ''}
|
|
268
|
+
<div>
|
|
269
|
+
<div style="font-size:13px; color:#94a3b8;">All-Time Spend</div>
|
|
270
|
+
<div style="margin-top:4px; font-size:18px; font-weight:700; color:#fbbf24;">$${summary.totalRealCost.toFixed(2)}</div>
|
|
271
|
+
</div>
|
|
272
|
+
${summary.totalCacheRead > 0 ? `<div>
|
|
273
|
+
<div style="font-size:13px; color:#94a3b8;">Cache Tokens</div>
|
|
274
|
+
<div style="margin-top:4px; font-size:14px;">${(summary.totalCacheRead||0).toLocaleString()} read<br/>${(summary.totalCacheWrite||0).toLocaleString()} write</div>
|
|
275
|
+
</div>` : ''}
|
|
276
|
+
${summary.totalEstimatedCost > 0 && summary.totalRealCost > summary.totalEstimatedCost * 1.1 ? `<div>
|
|
277
|
+
<div style="font-size:13px; color:#94a3b8;">sessions.json Gap</div>
|
|
278
|
+
<div style="margin-top:4px; font-size:14px; color:#f59e0b;">Estimate: $${summary.totalEstimatedCost.toFixed(4)}<br/>${(summary.totalRealCost / summary.totalEstimatedCost).toFixed(1)}x under-reported</div>
|
|
279
|
+
</div>` : ''}
|
|
280
|
+
${bleed > 0 ? `<div>
|
|
281
|
+
<div style="font-size:13px; color:#94a3b8;">Monthly Bleed</div>
|
|
282
|
+
<div style="margin-top:4px; font-size:18px; font-weight:700; color:#ef4444;">$${bleed.toFixed(2)}/mo</div>
|
|
283
|
+
</div>` : ''}
|
|
284
|
+
</div>
|
|
285
|
+
</div>
|
|
286
|
+
|
|
287
|
+
${(() => {
|
|
288
|
+
const wins = findings.filter(f => f.fix && f.severity !== 'info');
|
|
289
|
+
if (!wins.length) return '';
|
|
290
|
+
return `
|
|
291
|
+
<div class="section" style="border:1px solid #22c55e;">
|
|
292
|
+
<div class="section-title" style="color:#22c55e;">⚡ Quick Wins</div>
|
|
293
|
+
${wins.slice(0, 5).map((f, i) => `
|
|
294
|
+
<div style="margin-bottom:16px;">
|
|
295
|
+
<div style="color:#22c55e; font-weight:600; margin-bottom:4px;">${i+1}. ${f.fix}</div>
|
|
296
|
+
${f.command ? `<div style="background:#0f172a; color:#38bdf8; font-family:monospace; font-size:13px; padding:10px 14px; border-radius:6px; white-space:pre-wrap; border:1px solid #1e293b;"><span style="color:#6b7280; user-select:none;">$ </span>${f.command}</div>` : ''}
|
|
297
|
+
</div>
|
|
298
|
+
`).join('')}
|
|
299
|
+
</div>`;
|
|
300
|
+
})()}
|
|
247
301
|
|
|
248
302
|
</div>
|
|
249
303
|
<div class="footer">
|
package/src/analyzer.js
CHANGED
|
@@ -690,7 +690,7 @@ function analyzeSessions(sessionsPath) {
|
|
|
690
690
|
|
|
691
691
|
const ageMs = updatedAt ? Date.now() - new Date(updatedAt).getTime() : null;
|
|
692
692
|
const ageDays = ageMs ? ageMs / (1000 * 3600 * 24) : null;
|
|
693
|
-
const dailyCost = (ageDays && ageDays > 0
|
|
693
|
+
const dailyCost = (ageDays && ageDays > 1.0 && realCost > 0) ? realCost / ageDays : null;
|
|
694
694
|
|
|
695
695
|
const realModelKey = resolveModel(realModel);
|
|
696
696
|
breakdown.push({
|
package/src/htmlReport.js
CHANGED
|
@@ -69,7 +69,8 @@ async function generateHTMLReport(analysis, outPath) {
|
|
|
69
69
|
</div>
|
|
70
70
|
<div style="font-weight:600; color:#111; margin-bottom:4px">${f.message}</div>
|
|
71
71
|
${f.detail ? `<div style="color:#555; font-size:14px; margin-bottom:6px; white-space:pre-line">${f.detail}</div>` : ''}
|
|
72
|
-
${f.
|
|
72
|
+
${f.fix ? `<div style="color:#16a34a; font-size:14px; margin-top:8px;">→ ${f.fix}</div>` : ''}
|
|
73
|
+
${f.command ? `<div style="background:#0f172a; color:#38bdf8; font-family:monospace; font-size:13px; padding:10px 14px; border-radius:6px; margin-top:6px; white-space:pre-wrap; border:1px solid #1e293b;"><span style="color:#6b7280; user-select:none;">$ </span>${f.command}</div>` : ''}
|
|
73
74
|
</div>
|
|
74
75
|
`).join('');
|
|
75
76
|
|
|
@@ -243,7 +244,60 @@ async function generateHTMLReport(analysis, outPath) {
|
|
|
243
244
|
${untrackedHidden > 0 ? `<div style="margin-top:8px; font-size:12px; color:#64748b;">+ ${untrackedHidden} more not shown</div>` : ''}
|
|
244
245
|
</div>` : ''}
|
|
245
246
|
|
|
246
|
-
${burnSummary}
|
|
247
|
+
${burnSummary}
|
|
248
|
+
|
|
249
|
+
<div class="section">
|
|
250
|
+
<div class="section-title">Summary</div>
|
|
251
|
+
<div style="display:grid; grid-template-columns:repeat(auto-fit, minmax(200px, 1fr)); gap:16px;">
|
|
252
|
+
<div>
|
|
253
|
+
<div style="font-size:13px; color:#94a3b8;">Severity Breakdown</div>
|
|
254
|
+
<div style="margin-top:4px;">🔴 ${summary.critical} critical · 🟠 ${summary.high} high · 🟡 ${summary.medium} medium · 🔵 ${summary.low||0} low · ✅ ${summary.info} ok</div>
|
|
255
|
+
</div>
|
|
256
|
+
<div>
|
|
257
|
+
<div style="font-size:13px; color:#94a3b8;">Sessions Analyzed</div>
|
|
258
|
+
<div style="margin-top:4px; font-size:18px; font-weight:700;">${summary.sessionsAnalyzed}</div>
|
|
259
|
+
</div>
|
|
260
|
+
<div>
|
|
261
|
+
<div style="font-size:13px; color:#94a3b8;">Total Tokens</div>
|
|
262
|
+
<div style="margin-top:4px; font-size:18px; font-weight:700;">${(summary.totalTokensFound||0).toLocaleString()}</div>
|
|
263
|
+
</div>
|
|
264
|
+
${summary.todayCost > 0 ? `<div>
|
|
265
|
+
<div style="font-size:13px; color:#94a3b8;">Today's Spend</div>
|
|
266
|
+
<div style="margin-top:4px; font-size:18px; font-weight:700; color:#ef4444;">$${summary.todayCost.toFixed(2)}</div>
|
|
267
|
+
</div>` : ''}
|
|
268
|
+
<div>
|
|
269
|
+
<div style="font-size:13px; color:#94a3b8;">All-Time Spend</div>
|
|
270
|
+
<div style="margin-top:4px; font-size:18px; font-weight:700; color:#fbbf24;">$${summary.totalRealCost.toFixed(2)}</div>
|
|
271
|
+
</div>
|
|
272
|
+
${summary.totalCacheRead > 0 ? `<div>
|
|
273
|
+
<div style="font-size:13px; color:#94a3b8;">Cache Tokens</div>
|
|
274
|
+
<div style="margin-top:4px; font-size:14px;">${(summary.totalCacheRead||0).toLocaleString()} read<br/>${(summary.totalCacheWrite||0).toLocaleString()} write</div>
|
|
275
|
+
</div>` : ''}
|
|
276
|
+
${summary.totalEstimatedCost > 0 && summary.totalRealCost > summary.totalEstimatedCost * 1.1 ? `<div>
|
|
277
|
+
<div style="font-size:13px; color:#94a3b8;">sessions.json Gap</div>
|
|
278
|
+
<div style="margin-top:4px; font-size:14px; color:#f59e0b;">Estimate: $${summary.totalEstimatedCost.toFixed(4)}<br/>${(summary.totalRealCost / summary.totalEstimatedCost).toFixed(1)}x under-reported</div>
|
|
279
|
+
</div>` : ''}
|
|
280
|
+
${bleed > 0 ? `<div>
|
|
281
|
+
<div style="font-size:13px; color:#94a3b8;">Monthly Bleed</div>
|
|
282
|
+
<div style="margin-top:4px; font-size:18px; font-weight:700; color:#ef4444;">$${bleed.toFixed(2)}/mo</div>
|
|
283
|
+
</div>` : ''}
|
|
284
|
+
</div>
|
|
285
|
+
</div>
|
|
286
|
+
|
|
287
|
+
${(() => {
|
|
288
|
+
const wins = findings.filter(f => f.fix && f.severity !== 'info');
|
|
289
|
+
if (!wins.length) return '';
|
|
290
|
+
return `
|
|
291
|
+
<div class="section" style="border:1px solid #22c55e;">
|
|
292
|
+
<div class="section-title" style="color:#22c55e;">⚡ Quick Wins</div>
|
|
293
|
+
${wins.slice(0, 5).map((f, i) => `
|
|
294
|
+
<div style="margin-bottom:16px;">
|
|
295
|
+
<div style="color:#22c55e; font-weight:600; margin-bottom:4px;">${i+1}. ${f.fix}</div>
|
|
296
|
+
${f.command ? `<div style="background:#0f172a; color:#38bdf8; font-family:monospace; font-size:13px; padding:10px 14px; border-radius:6px; white-space:pre-wrap; border:1px solid #1e293b;"><span style="color:#6b7280; user-select:none;">$ </span>${f.command}</div>` : ''}
|
|
297
|
+
</div>
|
|
298
|
+
`).join('')}
|
|
299
|
+
</div>`;
|
|
300
|
+
})()}
|
|
247
301
|
|
|
248
302
|
</div>
|
|
249
303
|
<div class="footer">
|