claude-session-viewer 0.2.5 → 0.3.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/dist/client/assets/index-Ccni3k3B.js +69 -0
- package/dist/client/assets/index-Rji9IRSo.css +1 -0
- package/dist/client/index.html +2 -2
- package/dist/server/index.js +451 -0
- package/package.json +4 -1
- package/dist/client/assets/index-BOuaCrOZ.css +0 -1
- package/dist/client/assets/index-DSVrb86i.js +0 -51
|
@@ -0,0 +1 @@
|
|
|
1
|
+
*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.container{width:100%}@media (min-width: 640px){.container{max-width:640px}}@media (min-width: 768px){.container{max-width:768px}}@media (min-width: 1024px){.container{max-width:1024px}}@media (min-width: 1280px){.container{max-width:1280px}}@media (min-width: 1536px){.container{max-width:1536px}}.visible{visibility:visible}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.right-0{right:0}.top-0{top:0}.z-10{z-index:10}.mx-auto{margin-left:auto;margin-right:auto}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-6{margin-bottom:1.5rem}.mb-8{margin-bottom:2rem}.ml-2{margin-left:.5rem}.ml-6{margin-left:1.5rem}.ml-auto{margin-left:auto}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-4{margin-top:1rem}.mt-6{margin-top:1.5rem}.mt-8{margin-top:2rem}.line-clamp-2{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.h-12{height:3rem}.h-2{height:.5rem}.h-3{height:.75rem}.h-4{height:1rem}.h-6{height:1.5rem}.h-64{height:16rem}.h-8{height:2rem}.h-96{height:24rem}.h-full{height:100%}.h-screen{height:100vh}.w-1{width:.25rem}.w-1\/2{width:50%}.w-1\/3{width:33.333333%}.w-12{width:3rem}.w-3{width:.75rem}.w-3\/4{width:75%}.w-4{width:1rem}.w-full{width:100%}.min-w-0{min-width:0px}.max-w-4xl{max-width:56rem}.max-w-7xl{max-width:80rem}.flex-1{flex:1 1 0%}.flex-shrink-0{flex-shrink:0}.rotate-90{--tw-rotate: 90deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}.cursor-col-resize{cursor:col-resize}.auto-rows-fr{grid-auto-rows:minmax(0,1fr)}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-6{gap:1.5rem}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem * var(--tw-space-y-reverse))}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-l{border-left-width:1px}.border-l-2{border-left-width:2px}.border-r{border-right-width:1px}.border-blue-400{--tw-border-opacity: 1;border-color:rgb(96 165 250 / var(--tw-border-opacity, 1))}.border-blue-500{--tw-border-opacity: 1;border-color:rgb(59 130 246 / var(--tw-border-opacity, 1))}.border-blue-500\/20{border-color:#3b82f633}.border-cyan-500\/20{border-color:#06b6d433}.border-gray-700{--tw-border-opacity: 1;border-color:rgb(55 65 81 / var(--tw-border-opacity, 1))}.border-green-500\/20{border-color:#22c55e33}.border-orange-500\/20{border-color:#f9731633}.border-pink-500\/20{border-color:#ec489933}.border-purple-500\/20{border-color:#a855f733}.border-transparent{border-color:transparent}.bg-blue-500{--tw-bg-opacity: 1;background-color:rgb(59 130 246 / var(--tw-bg-opacity, 1))}.bg-blue-500\/10{background-color:#3b82f61a}.bg-blue-600{--tw-bg-opacity: 1;background-color:rgb(37 99 235 / var(--tw-bg-opacity, 1))}.bg-blue-900{--tw-bg-opacity: 1;background-color:rgb(30 58 138 / var(--tw-bg-opacity, 1))}.bg-blue-900\/50{background-color:#1e3a8a80}.bg-cyan-500{--tw-bg-opacity: 1;background-color:rgb(6 182 212 / var(--tw-bg-opacity, 1))}.bg-cyan-500\/10{background-color:#06b6d41a}.bg-gray-700{--tw-bg-opacity: 1;background-color:rgb(55 65 81 / var(--tw-bg-opacity, 1))}.bg-gray-800{--tw-bg-opacity: 1;background-color:rgb(31 41 55 / var(--tw-bg-opacity, 1))}.bg-gray-800\/30{background-color:#1f29374d}.bg-gray-800\/50{background-color:#1f293780}.bg-gray-900{--tw-bg-opacity: 1;background-color:rgb(17 24 39 / var(--tw-bg-opacity, 1))}.bg-gray-900\/50{background-color:#11182780}.bg-gray-900\/70{background-color:#111827b3}.bg-green-500{--tw-bg-opacity: 1;background-color:rgb(34 197 94 / var(--tw-bg-opacity, 1))}.bg-green-500\/10{background-color:#22c55e1a}.bg-green-900{--tw-bg-opacity: 1;background-color:rgb(20 83 45 / var(--tw-bg-opacity, 1))}.bg-green-900\/30{background-color:#14532d4d}.bg-green-900\/50{background-color:#14532d80}.bg-orange-500{--tw-bg-opacity: 1;background-color:rgb(249 115 22 / var(--tw-bg-opacity, 1))}.bg-orange-500\/10{background-color:#f973161a}.bg-pink-500{--tw-bg-opacity: 1;background-color:rgb(236 72 153 / var(--tw-bg-opacity, 1))}.bg-pink-500\/10{background-color:#ec48991a}.bg-purple-500{--tw-bg-opacity: 1;background-color:rgb(168 85 247 / var(--tw-bg-opacity, 1))}.bg-purple-500\/10{background-color:#a855f71a}.bg-purple-700{--tw-bg-opacity: 1;background-color:rgb(126 34 206 / var(--tw-bg-opacity, 1))}.bg-purple-900\/50{background-color:#581c8780}.bg-red-500{--tw-bg-opacity: 1;background-color:rgb(239 68 68 / var(--tw-bg-opacity, 1))}.bg-transparent{background-color:transparent}.bg-yellow-500{--tw-bg-opacity: 1;background-color:rgb(234 179 8 / var(--tw-bg-opacity, 1))}.bg-yellow-900\/30{background-color:#713f124d}.bg-gradient-to-r{background-image:linear-gradient(to right,var(--tw-gradient-stops))}.from-blue-500{--tw-gradient-from: #3b82f6 var(--tw-gradient-from-position);--tw-gradient-to: rgb(59 130 246 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.to-green-500{--tw-gradient-to: #22c55e var(--tw-gradient-to-position)}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.p-8{padding:2rem}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.pb-4{padding-bottom:1rem}.pl-14{padding-left:3.5rem}.pl-4{padding-left:1rem}.pl-8{padding-left:2rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-4xl{font-size:2.25rem;line-height:2.5rem}.text-\[10px\]{font-size:10px}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.leading-tight{line-height:1.25}.text-blue-200{--tw-text-opacity: 1;color:rgb(191 219 254 / var(--tw-text-opacity, 1))}.text-blue-300{--tw-text-opacity: 1;color:rgb(147 197 253 / var(--tw-text-opacity, 1))}.text-blue-400{--tw-text-opacity: 1;color:rgb(96 165 250 / var(--tw-text-opacity, 1))}.text-cyan-400{--tw-text-opacity: 1;color:rgb(34 211 238 / var(--tw-text-opacity, 1))}.text-gray-200{--tw-text-opacity: 1;color:rgb(229 231 235 / var(--tw-text-opacity, 1))}.text-gray-300{--tw-text-opacity: 1;color:rgb(209 213 219 / var(--tw-text-opacity, 1))}.text-gray-400{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity, 1))}.text-gray-500{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity, 1))}.text-gray-600{--tw-text-opacity: 1;color:rgb(75 85 99 / var(--tw-text-opacity, 1))}.text-green-200{--tw-text-opacity: 1;color:rgb(187 247 208 / var(--tw-text-opacity, 1))}.text-green-300{--tw-text-opacity: 1;color:rgb(134 239 172 / var(--tw-text-opacity, 1))}.text-green-400{--tw-text-opacity: 1;color:rgb(74 222 128 / var(--tw-text-opacity, 1))}.text-purple-100{--tw-text-opacity: 1;color:rgb(243 232 255 / var(--tw-text-opacity, 1))}.text-purple-300{--tw-text-opacity: 1;color:rgb(216 180 254 / var(--tw-text-opacity, 1))}.text-purple-400{--tw-text-opacity: 1;color:rgb(192 132 252 / var(--tw-text-opacity, 1))}.text-red-400{--tw-text-opacity: 1;color:rgb(248 113 113 / var(--tw-text-opacity, 1))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.text-yellow-400{--tw-text-opacity: 1;color:rgb(250 204 21 / var(--tw-text-opacity, 1))}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-200{transition-duration:.2s}.duration-300{transition-duration:.3s}.\@container{container-type:inline-size}:root{font-family:Inter,system-ui,Avenir,Helvetica,Arial,sans-serif;line-height:1.5;font-weight:400;color-scheme:light dark;color:#ffffffde;background-color:#242424;font-synthesis:none;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}body{margin:0;display:flex;place-items:center;min-width:320px;min-height:100vh}#root{width:100%;min-height:100vh}*{scrollbar-width:thin;scrollbar-color:#4B5563 #1F2937}*::-webkit-scrollbar{width:8px;height:8px}*::-webkit-scrollbar-track{background:#1f2937}*::-webkit-scrollbar-thumb{background-color:#4b5563;border-radius:4px}*::-webkit-scrollbar-thumb:hover{background-color:#6b7280}.last\:border-b-0:last-child{border-bottom-width:0px}.last\:pb-0:last-child{padding-bottom:0}.hover\:bg-gray-700:hover{--tw-bg-opacity: 1;background-color:rgb(55 65 81 / var(--tw-bg-opacity, 1))}.hover\:bg-gray-700\/50:hover{background-color:#37415180}.hover\:bg-gray-800:hover{--tw-bg-opacity: 1;background-color:rgb(31 41 55 / var(--tw-bg-opacity, 1))}.hover\:bg-purple-600:hover{--tw-bg-opacity: 1;background-color:rgb(147 51 234 / var(--tw-bg-opacity, 1))}.hover\:text-blue-400:hover{--tw-text-opacity: 1;color:rgb(96 165 250 / var(--tw-text-opacity, 1))}.hover\:text-gray-200:hover{--tw-text-opacity: 1;color:rgb(229 231 235 / var(--tw-text-opacity, 1))}.hover\:text-gray-300:hover{--tw-text-opacity: 1;color:rgb(209 213 219 / var(--tw-text-opacity, 1))}.group:hover .group-hover\:bg-blue-500{--tw-bg-opacity: 1;background-color:rgb(59 130 246 / var(--tw-bg-opacity, 1))}@container (min-width: 300px){.\@\[300px\]\:inline{display:inline}.\@\[300px\]\:hidden{display:none}}@media (min-width: 768px){.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (min-width: 1024px){.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}@media (min-width: 1280px){.xl\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}}
|
package/dist/client/index.html
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
7
|
<title>Claude Session Viewer</title>
|
|
8
|
-
<script type="module" crossorigin src="/assets/index-
|
|
9
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-Ccni3k3B.js"></script>
|
|
9
|
+
<link rel="stylesheet" crossorigin href="/assets/index-Rji9IRSo.css">
|
|
10
10
|
</head>
|
|
11
11
|
<body>
|
|
12
12
|
<div id="root"></div>
|
package/dist/server/index.js
CHANGED
|
@@ -221,6 +221,457 @@ async function getProjectSessions(projectPath) {
|
|
|
221
221
|
}
|
|
222
222
|
return allSessions;
|
|
223
223
|
}
|
|
224
|
+
// Claude API Pricing (as of January 2025)
|
|
225
|
+
// Prices per 1M tokens
|
|
226
|
+
const PRICING = {
|
|
227
|
+
'claude-sonnet-4-5-20250929': {
|
|
228
|
+
input: 3.0,
|
|
229
|
+
output: 15.0,
|
|
230
|
+
cacheCreation: 3.75,
|
|
231
|
+
cacheRead: 0.30
|
|
232
|
+
},
|
|
233
|
+
'claude-sonnet-4-20250514': {
|
|
234
|
+
input: 3.0,
|
|
235
|
+
output: 15.0,
|
|
236
|
+
cacheCreation: 3.75,
|
|
237
|
+
cacheRead: 0.30
|
|
238
|
+
},
|
|
239
|
+
'claude-opus-4-20250514': {
|
|
240
|
+
input: 15.0,
|
|
241
|
+
output: 75.0,
|
|
242
|
+
cacheCreation: 18.75,
|
|
243
|
+
cacheRead: 1.50
|
|
244
|
+
},
|
|
245
|
+
'claude-haiku-4-20250515': {
|
|
246
|
+
input: 0.80,
|
|
247
|
+
output: 4.0,
|
|
248
|
+
cacheCreation: 1.0,
|
|
249
|
+
cacheRead: 0.08
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
// Helper: Calculate cost for token usage
|
|
253
|
+
function calculateCost(usage, model) {
|
|
254
|
+
const pricing = PRICING[model] || PRICING['claude-sonnet-4-5-20250929'];
|
|
255
|
+
return {
|
|
256
|
+
inputCost: (usage.inputTokens / 1000000) * pricing.input,
|
|
257
|
+
outputCost: (usage.outputTokens / 1000000) * pricing.output,
|
|
258
|
+
cacheCreationCost: (usage.cacheCreationTokens / 1000000) * pricing.cacheCreation,
|
|
259
|
+
cacheReadCost: (usage.cacheReadTokens / 1000000) * pricing.cacheRead,
|
|
260
|
+
totalCost: (usage.inputTokens / 1000000) * pricing.input +
|
|
261
|
+
(usage.outputTokens / 1000000) * pricing.output +
|
|
262
|
+
(usage.cacheCreationTokens / 1000000) * pricing.cacheCreation +
|
|
263
|
+
(usage.cacheReadTokens / 1000000) * pricing.cacheRead
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
// Helper: Extract token usage from message
|
|
267
|
+
function extractTokenUsage(message) {
|
|
268
|
+
if (message.type !== 'assistant' || !message.message?.usage) {
|
|
269
|
+
return null;
|
|
270
|
+
}
|
|
271
|
+
const usage = message.message.usage;
|
|
272
|
+
const model = message.message.model || 'unknown';
|
|
273
|
+
return {
|
|
274
|
+
usage: {
|
|
275
|
+
inputTokens: usage.input_tokens || 0,
|
|
276
|
+
cacheCreationTokens: usage.cache_creation_input_tokens || 0,
|
|
277
|
+
cacheReadTokens: usage.cache_read_input_tokens || 0,
|
|
278
|
+
outputTokens: usage.output_tokens || 0,
|
|
279
|
+
totalTokens: (usage.input_tokens || 0) +
|
|
280
|
+
(usage.cache_creation_input_tokens || 0) +
|
|
281
|
+
(usage.cache_read_input_tokens || 0) +
|
|
282
|
+
(usage.output_tokens || 0)
|
|
283
|
+
},
|
|
284
|
+
model
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
// Helper: Aggregate token usage
|
|
288
|
+
function aggregateTokenUsage(usages) {
|
|
289
|
+
return usages.reduce((acc, usage) => ({
|
|
290
|
+
inputTokens: acc.inputTokens + usage.inputTokens,
|
|
291
|
+
cacheCreationTokens: acc.cacheCreationTokens + usage.cacheCreationTokens,
|
|
292
|
+
cacheReadTokens: acc.cacheReadTokens + usage.cacheReadTokens,
|
|
293
|
+
outputTokens: acc.outputTokens + usage.outputTokens,
|
|
294
|
+
totalTokens: acc.totalTokens + usage.totalTokens
|
|
295
|
+
}), {
|
|
296
|
+
inputTokens: 0,
|
|
297
|
+
cacheCreationTokens: 0,
|
|
298
|
+
cacheReadTokens: 0,
|
|
299
|
+
outputTokens: 0,
|
|
300
|
+
totalTokens: 0
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
// API: Get token statistics
|
|
304
|
+
server.get('/api/statistics/tokens', async (request, reply) => {
|
|
305
|
+
try {
|
|
306
|
+
const projectsDir = join(CLAUDE_DIR, 'projects');
|
|
307
|
+
const projects = await readdir(projectsDir);
|
|
308
|
+
// Parse days parameter (default to 30)
|
|
309
|
+
const daysParam = request.query.days || '30';
|
|
310
|
+
const cutoffDate = new Date();
|
|
311
|
+
if (daysParam !== 'all') {
|
|
312
|
+
const days = parseInt(daysParam, 10);
|
|
313
|
+
cutoffDate.setDate(cutoffDate.getDate() - days);
|
|
314
|
+
cutoffDate.setHours(0, 0, 0, 0);
|
|
315
|
+
}
|
|
316
|
+
else {
|
|
317
|
+
// For 'all', set to a very old date to include everything
|
|
318
|
+
cutoffDate.setFullYear(2000, 0, 1);
|
|
319
|
+
}
|
|
320
|
+
// Data structures for aggregation
|
|
321
|
+
const dailyMap = new Map();
|
|
322
|
+
const projectMap = new Map();
|
|
323
|
+
const modelMap = new Map();
|
|
324
|
+
let totalMessages = 0;
|
|
325
|
+
let totalSessions = 0;
|
|
326
|
+
let minDate = null;
|
|
327
|
+
let maxDate = null;
|
|
328
|
+
let totalCacheCreation = 0;
|
|
329
|
+
let totalCacheRead = 0;
|
|
330
|
+
let ephemeral5mTokens = 0;
|
|
331
|
+
let ephemeral1hTokens = 0;
|
|
332
|
+
const allUsages = [];
|
|
333
|
+
const allCosts = [];
|
|
334
|
+
// Productivity metrics
|
|
335
|
+
const toolUsageMap = new Map();
|
|
336
|
+
let totalAgentSessions = 0;
|
|
337
|
+
// Trend analysis data structures
|
|
338
|
+
const hourlyMap = new Map();
|
|
339
|
+
const weekdayMap = new Map();
|
|
340
|
+
// Initialize hourly map (0-23)
|
|
341
|
+
for (let hour = 0; hour < 24; hour++) {
|
|
342
|
+
hourlyMap.set(hour, {
|
|
343
|
+
usage: { inputTokens: 0, cacheCreationTokens: 0, cacheReadTokens: 0, outputTokens: 0, totalTokens: 0 },
|
|
344
|
+
sessionIds: new Set(),
|
|
345
|
+
messageCount: 0
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
// Initialize weekday map (0-6, Sunday-Saturday)
|
|
349
|
+
for (let weekday = 0; weekday < 7; weekday++) {
|
|
350
|
+
weekdayMap.set(weekday, {
|
|
351
|
+
usage: { inputTokens: 0, cacheCreationTokens: 0, cacheReadTokens: 0, outputTokens: 0, totalTokens: 0 },
|
|
352
|
+
sessionIds: new Set(),
|
|
353
|
+
messageCount: 0
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
// Process all projects
|
|
357
|
+
for (const project of projects) {
|
|
358
|
+
const projectPath = join(projectsDir, project);
|
|
359
|
+
const projectStat = await stat(projectPath);
|
|
360
|
+
if (!projectStat.isDirectory())
|
|
361
|
+
continue;
|
|
362
|
+
const files = await readdir(projectPath);
|
|
363
|
+
const displayName = getProjectDisplayName(project);
|
|
364
|
+
// Initialize project entry
|
|
365
|
+
if (!projectMap.has(project)) {
|
|
366
|
+
projectMap.set(project, {
|
|
367
|
+
usage: {
|
|
368
|
+
inputTokens: 0,
|
|
369
|
+
cacheCreationTokens: 0,
|
|
370
|
+
cacheReadTokens: 0,
|
|
371
|
+
outputTokens: 0,
|
|
372
|
+
totalTokens: 0
|
|
373
|
+
},
|
|
374
|
+
sessionIds: new Set(),
|
|
375
|
+
displayName
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
for (const file of files) {
|
|
379
|
+
if (!file.endsWith('.jsonl'))
|
|
380
|
+
continue;
|
|
381
|
+
const filePath = join(projectPath, file);
|
|
382
|
+
const fileStat = await stat(filePath);
|
|
383
|
+
// Skip empty files
|
|
384
|
+
if (fileStat.size === 0)
|
|
385
|
+
continue;
|
|
386
|
+
// Skip agent sessions (they're counted in parent sessions)
|
|
387
|
+
const sessionId = file.replace('.jsonl', '');
|
|
388
|
+
if (sessionId.startsWith('agent-'))
|
|
389
|
+
continue;
|
|
390
|
+
try {
|
|
391
|
+
const messages = await parseJsonl(filePath);
|
|
392
|
+
// Skip sessions with only 1 assistant message
|
|
393
|
+
if (messages.length === 1 && messages[0].type === 'assistant') {
|
|
394
|
+
continue;
|
|
395
|
+
}
|
|
396
|
+
totalSessions++;
|
|
397
|
+
const projectData = projectMap.get(project);
|
|
398
|
+
projectData.sessionIds.add(sessionId);
|
|
399
|
+
// Check if this session has agent sessions
|
|
400
|
+
const agentDescriptions = collectAgentDescriptions(messages);
|
|
401
|
+
if (agentDescriptions.size > 0) {
|
|
402
|
+
totalAgentSessions++;
|
|
403
|
+
}
|
|
404
|
+
// Process each message
|
|
405
|
+
for (const message of messages) {
|
|
406
|
+
const tokenData = extractTokenUsage(message);
|
|
407
|
+
if (!tokenData)
|
|
408
|
+
continue;
|
|
409
|
+
// Aggregate by date
|
|
410
|
+
const messageDate = new Date(message.timestamp);
|
|
411
|
+
// Skip messages older than cutoff date
|
|
412
|
+
if (messageDate < cutoffDate)
|
|
413
|
+
continue;
|
|
414
|
+
totalMessages++;
|
|
415
|
+
const { usage, model } = tokenData;
|
|
416
|
+
// Aggregate overall
|
|
417
|
+
allUsages.push(usage);
|
|
418
|
+
allCosts.push(calculateCost(usage, model));
|
|
419
|
+
const dateKey = messageDate.toISOString().split('T')[0]; // YYYY-MM-DD
|
|
420
|
+
if (!minDate || messageDate < minDate)
|
|
421
|
+
minDate = messageDate;
|
|
422
|
+
if (!maxDate || messageDate > maxDate)
|
|
423
|
+
maxDate = messageDate;
|
|
424
|
+
if (!dailyMap.has(dateKey)) {
|
|
425
|
+
dailyMap.set(dateKey, {
|
|
426
|
+
usage: { inputTokens: 0, cacheCreationTokens: 0, cacheReadTokens: 0, outputTokens: 0, totalTokens: 0 },
|
|
427
|
+
sessionIds: new Set()
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
const dailyData = dailyMap.get(dateKey);
|
|
431
|
+
dailyData.usage.inputTokens += usage.inputTokens;
|
|
432
|
+
dailyData.usage.cacheCreationTokens += usage.cacheCreationTokens;
|
|
433
|
+
dailyData.usage.cacheReadTokens += usage.cacheReadTokens;
|
|
434
|
+
dailyData.usage.outputTokens += usage.outputTokens;
|
|
435
|
+
dailyData.usage.totalTokens += usage.totalTokens;
|
|
436
|
+
dailyData.sessionIds.add(sessionId);
|
|
437
|
+
// Aggregate by hour of day
|
|
438
|
+
const hour = messageDate.getHours();
|
|
439
|
+
const hourData = hourlyMap.get(hour);
|
|
440
|
+
hourData.usage.inputTokens += usage.inputTokens;
|
|
441
|
+
hourData.usage.cacheCreationTokens += usage.cacheCreationTokens;
|
|
442
|
+
hourData.usage.cacheReadTokens += usage.cacheReadTokens;
|
|
443
|
+
hourData.usage.outputTokens += usage.outputTokens;
|
|
444
|
+
hourData.usage.totalTokens += usage.totalTokens;
|
|
445
|
+
hourData.sessionIds.add(sessionId);
|
|
446
|
+
hourData.messageCount++;
|
|
447
|
+
// Aggregate by day of week
|
|
448
|
+
const weekday = messageDate.getDay();
|
|
449
|
+
const weekdayData = weekdayMap.get(weekday);
|
|
450
|
+
weekdayData.usage.inputTokens += usage.inputTokens;
|
|
451
|
+
weekdayData.usage.cacheCreationTokens += usage.cacheCreationTokens;
|
|
452
|
+
weekdayData.usage.cacheReadTokens += usage.cacheReadTokens;
|
|
453
|
+
weekdayData.usage.outputTokens += usage.outputTokens;
|
|
454
|
+
weekdayData.usage.totalTokens += usage.totalTokens;
|
|
455
|
+
weekdayData.sessionIds.add(sessionId);
|
|
456
|
+
weekdayData.messageCount++;
|
|
457
|
+
// Aggregate by project
|
|
458
|
+
projectData.usage.inputTokens += usage.inputTokens;
|
|
459
|
+
projectData.usage.cacheCreationTokens += usage.cacheCreationTokens;
|
|
460
|
+
projectData.usage.cacheReadTokens += usage.cacheReadTokens;
|
|
461
|
+
projectData.usage.outputTokens += usage.outputTokens;
|
|
462
|
+
projectData.usage.totalTokens += usage.totalTokens;
|
|
463
|
+
// Aggregate by model
|
|
464
|
+
if (!modelMap.has(model)) {
|
|
465
|
+
modelMap.set(model, {
|
|
466
|
+
usage: { inputTokens: 0, cacheCreationTokens: 0, cacheReadTokens: 0, outputTokens: 0, totalTokens: 0 },
|
|
467
|
+
messageCount: 0
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
const modelData = modelMap.get(model);
|
|
471
|
+
modelData.usage.inputTokens += usage.inputTokens;
|
|
472
|
+
modelData.usage.cacheCreationTokens += usage.cacheCreationTokens;
|
|
473
|
+
modelData.usage.cacheReadTokens += usage.cacheReadTokens;
|
|
474
|
+
modelData.usage.outputTokens += usage.outputTokens;
|
|
475
|
+
modelData.usage.totalTokens += usage.totalTokens;
|
|
476
|
+
modelData.messageCount++;
|
|
477
|
+
// Cache stats
|
|
478
|
+
totalCacheCreation += usage.cacheCreationTokens;
|
|
479
|
+
totalCacheRead += usage.cacheReadTokens;
|
|
480
|
+
const cacheCreation = message.message?.usage?.cache_creation;
|
|
481
|
+
if (cacheCreation) {
|
|
482
|
+
ephemeral5mTokens += cacheCreation.ephemeral_5m_input_tokens || 0;
|
|
483
|
+
ephemeral1hTokens += cacheCreation.ephemeral_1h_input_tokens || 0;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
// Track tool usage for all messages
|
|
487
|
+
const toolUseIds = new Set();
|
|
488
|
+
const successfulToolUseIds = new Set();
|
|
489
|
+
// First pass: collect all tool_use instances
|
|
490
|
+
for (const message of messages) {
|
|
491
|
+
// Skip messages older than cutoff date
|
|
492
|
+
const messageDate = new Date(message.timestamp);
|
|
493
|
+
if (messageDate < cutoffDate)
|
|
494
|
+
continue;
|
|
495
|
+
if (message.type === 'assistant' && message.message?.content && Array.isArray(message.message.content)) {
|
|
496
|
+
for (const item of message.message.content) {
|
|
497
|
+
if (item.type === 'tool_use' && item.name && item.id) {
|
|
498
|
+
const toolName = item.name;
|
|
499
|
+
if (!toolUsageMap.has(toolName)) {
|
|
500
|
+
toolUsageMap.set(toolName, { total: 0, successful: 0 });
|
|
501
|
+
}
|
|
502
|
+
// Only count each tool_use_id once
|
|
503
|
+
if (!toolUseIds.has(item.id)) {
|
|
504
|
+
toolUseIds.add(item.id);
|
|
505
|
+
const toolStats = toolUsageMap.get(toolName);
|
|
506
|
+
toolStats.total++;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
// Second pass: find successful tool results
|
|
513
|
+
for (const message of messages) {
|
|
514
|
+
// Skip messages older than cutoff date
|
|
515
|
+
const messageDate = new Date(message.timestamp);
|
|
516
|
+
if (messageDate < cutoffDate)
|
|
517
|
+
continue;
|
|
518
|
+
if (message.message?.content && Array.isArray(message.message.content)) {
|
|
519
|
+
for (const item of message.message.content) {
|
|
520
|
+
if (item.type === 'tool_result' && !item.is_error && item.tool_use_id) {
|
|
521
|
+
// Only count each successful tool_result once
|
|
522
|
+
if (!successfulToolUseIds.has(item.tool_use_id)) {
|
|
523
|
+
successfulToolUseIds.add(item.tool_use_id);
|
|
524
|
+
// Find the corresponding tool_use to get the tool name
|
|
525
|
+
let found = false;
|
|
526
|
+
for (const msg of messages) {
|
|
527
|
+
if (msg.type === 'assistant' && msg.message?.content && Array.isArray(msg.message.content)) {
|
|
528
|
+
for (const toolUseItem of msg.message.content) {
|
|
529
|
+
if (toolUseItem.type === 'tool_use' && toolUseItem.id === item.tool_use_id && toolUseItem.name) {
|
|
530
|
+
const toolName = toolUseItem.name;
|
|
531
|
+
const toolStats = toolUsageMap.get(toolName);
|
|
532
|
+
if (toolStats) {
|
|
533
|
+
toolStats.successful++;
|
|
534
|
+
}
|
|
535
|
+
found = true;
|
|
536
|
+
break;
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
if (found)
|
|
540
|
+
break;
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
catch (error) {
|
|
550
|
+
console.error(`Error processing ${file}:`, error);
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
// Calculate overall totals
|
|
555
|
+
const totalUsage = aggregateTokenUsage(allUsages);
|
|
556
|
+
const totalCost = allCosts.reduce((acc, cost) => ({
|
|
557
|
+
inputCost: acc.inputCost + cost.inputCost,
|
|
558
|
+
outputCost: acc.outputCost + cost.outputCost,
|
|
559
|
+
cacheCreationCost: acc.cacheCreationCost + cost.cacheCreationCost,
|
|
560
|
+
cacheReadCost: acc.cacheReadCost + cost.cacheReadCost,
|
|
561
|
+
totalCost: acc.totalCost + cost.totalCost
|
|
562
|
+
}), { inputCost: 0, outputCost: 0, cacheCreationCost: 0, cacheReadCost: 0, totalCost: 0 });
|
|
563
|
+
// Calculate cache efficiency
|
|
564
|
+
const totalPotentialInput = totalUsage.inputTokens + totalCacheCreation + totalCacheRead;
|
|
565
|
+
const cacheHitRate = totalPotentialInput > 0 ? (totalCacheRead / totalPotentialInput) * 100 : 0;
|
|
566
|
+
// Estimate savings (cache read is ~10x cheaper than regular input for Sonnet)
|
|
567
|
+
const savedCost = (totalCacheRead / 1000000) * (PRICING['claude-sonnet-4-5-20250929'].input - PRICING['claude-sonnet-4-5-20250929'].cacheRead);
|
|
568
|
+
// Convert maps to arrays
|
|
569
|
+
const daily = Array.from(dailyMap.entries())
|
|
570
|
+
.map(([date, data]) => ({
|
|
571
|
+
date,
|
|
572
|
+
usage: data.usage,
|
|
573
|
+
sessionCount: data.sessionIds.size
|
|
574
|
+
}))
|
|
575
|
+
.sort((a, b) => a.date.localeCompare(b.date));
|
|
576
|
+
const byProject = Array.from(projectMap.entries())
|
|
577
|
+
.map(([project, data]) => ({
|
|
578
|
+
project,
|
|
579
|
+
displayName: data.displayName,
|
|
580
|
+
usage: data.usage,
|
|
581
|
+
sessionCount: data.sessionIds.size
|
|
582
|
+
}))
|
|
583
|
+
.sort((a, b) => b.usage.totalTokens - a.usage.totalTokens);
|
|
584
|
+
const byModel = Array.from(modelMap.entries())
|
|
585
|
+
.map(([model, data]) => ({
|
|
586
|
+
model,
|
|
587
|
+
usage: data.usage,
|
|
588
|
+
messageCount: data.messageCount
|
|
589
|
+
}))
|
|
590
|
+
.sort((a, b) => b.usage.totalTokens - a.usage.totalTokens);
|
|
591
|
+
// Calculate productivity metrics
|
|
592
|
+
const toolUsage = Array.from(toolUsageMap.entries())
|
|
593
|
+
.map(([toolName, stats]) => ({
|
|
594
|
+
toolName,
|
|
595
|
+
totalUses: stats.total,
|
|
596
|
+
successfulUses: stats.successful,
|
|
597
|
+
successRate: stats.total > 0 ? (stats.successful / stats.total) * 100 : 0
|
|
598
|
+
}))
|
|
599
|
+
.sort((a, b) => b.totalUses - a.totalUses);
|
|
600
|
+
// Debug: log tools with success rate > 100%
|
|
601
|
+
const problematicTools = toolUsage.filter(t => t.successRate > 100);
|
|
602
|
+
if (problematicTools.length > 0) {
|
|
603
|
+
console.log('⚠️ Tools with success rate > 100%:', JSON.stringify(problematicTools, null, 2));
|
|
604
|
+
}
|
|
605
|
+
const totalToolCalls = Array.from(toolUsageMap.values()).reduce((sum, stats) => sum + stats.total, 0);
|
|
606
|
+
const agentUsageRate = totalSessions > 0 ? (totalAgentSessions / totalSessions) * 100 : 0;
|
|
607
|
+
// Convert trend maps to arrays
|
|
608
|
+
const weekdayNames = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
|
|
609
|
+
const byHour = Array.from(hourlyMap.entries())
|
|
610
|
+
.map(([hour, data]) => ({
|
|
611
|
+
hour,
|
|
612
|
+
sessionCount: data.sessionIds.size,
|
|
613
|
+
messageCount: data.messageCount,
|
|
614
|
+
usage: data.usage
|
|
615
|
+
}))
|
|
616
|
+
.sort((a, b) => a.hour - b.hour);
|
|
617
|
+
const byWeekday = Array.from(weekdayMap.entries())
|
|
618
|
+
.map(([weekday, data]) => ({
|
|
619
|
+
weekday,
|
|
620
|
+
weekdayName: weekdayNames[weekday],
|
|
621
|
+
sessionCount: data.sessionIds.size,
|
|
622
|
+
messageCount: data.messageCount,
|
|
623
|
+
usage: data.usage
|
|
624
|
+
}))
|
|
625
|
+
.sort((a, b) => a.weekday - b.weekday);
|
|
626
|
+
const statistics = {
|
|
627
|
+
overview: {
|
|
628
|
+
total: totalUsage,
|
|
629
|
+
totalSessions,
|
|
630
|
+
totalMessages,
|
|
631
|
+
dateRange: {
|
|
632
|
+
start: minDate?.toISOString() || new Date().toISOString(),
|
|
633
|
+
end: maxDate?.toISOString() || new Date().toISOString()
|
|
634
|
+
}
|
|
635
|
+
},
|
|
636
|
+
daily,
|
|
637
|
+
byProject,
|
|
638
|
+
byModel,
|
|
639
|
+
cache: {
|
|
640
|
+
totalCacheCreation,
|
|
641
|
+
totalCacheRead,
|
|
642
|
+
ephemeral5mTokens,
|
|
643
|
+
ephemeral1hTokens,
|
|
644
|
+
cacheHitRate,
|
|
645
|
+
estimatedSavings: savedCost
|
|
646
|
+
},
|
|
647
|
+
cost: totalCost,
|
|
648
|
+
productivity: {
|
|
649
|
+
toolUsage,
|
|
650
|
+
totalToolCalls,
|
|
651
|
+
agentSessions: totalAgentSessions,
|
|
652
|
+
totalSessions,
|
|
653
|
+
agentUsageRate
|
|
654
|
+
},
|
|
655
|
+
trends: {
|
|
656
|
+
byHour,
|
|
657
|
+
byWeekday
|
|
658
|
+
}
|
|
659
|
+
};
|
|
660
|
+
console.log('Token statistics summary:', {
|
|
661
|
+
totalSessions,
|
|
662
|
+
totalMessages,
|
|
663
|
+
dailyEntries: daily.length,
|
|
664
|
+
projectEntries: byProject.length,
|
|
665
|
+
modelEntries: byModel.length,
|
|
666
|
+
sampleDaily: daily[0]
|
|
667
|
+
});
|
|
668
|
+
return statistics;
|
|
669
|
+
}
|
|
670
|
+
catch (error) {
|
|
671
|
+
console.error('Error calculating token statistics:', error);
|
|
672
|
+
return reply.code(500).send({ error: 'Internal server error' });
|
|
673
|
+
}
|
|
674
|
+
});
|
|
224
675
|
// API: Get all sessions grouped by project
|
|
225
676
|
server.get('/api/sessions', async (request, reply) => {
|
|
226
677
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-session-viewer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"claude-session-viewer": "./bin/cli.js"
|
|
@@ -44,6 +44,7 @@
|
|
|
44
44
|
"@fastify/static": "^7.0.3",
|
|
45
45
|
"@fastify/websocket": "^10.0.1",
|
|
46
46
|
"@tanstack/react-query": "^5.17.19",
|
|
47
|
+
"chart.js": "^4.5.1",
|
|
47
48
|
"chokidar": "^3.5.3",
|
|
48
49
|
"date-fns": "^3.0.6",
|
|
49
50
|
"fastify": "^4.25.2",
|
|
@@ -53,12 +54,14 @@
|
|
|
53
54
|
"marked": "^11.1.1",
|
|
54
55
|
"open": "^11.0.0",
|
|
55
56
|
"react": "^18.2.0",
|
|
57
|
+
"react-chartjs-2": "^5.3.1",
|
|
56
58
|
"react-dom": "^18.2.0",
|
|
57
59
|
"react-router-dom": "^7.11.0",
|
|
58
60
|
"react-window": "^1.8.10",
|
|
59
61
|
"zustand": "^4.4.7"
|
|
60
62
|
},
|
|
61
63
|
"devDependencies": {
|
|
64
|
+
"@tailwindcss/container-queries": "^0.1.1",
|
|
62
65
|
"@types/node": "^20.10.6",
|
|
63
66
|
"@types/react": "^18.2.46",
|
|
64
67
|
"@types/react-dom": "^18.2.18",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.container{width:100%}@media (min-width: 640px){.container{max-width:640px}}@media (min-width: 768px){.container{max-width:768px}}@media (min-width: 1024px){.container{max-width:1024px}}@media (min-width: 1280px){.container{max-width:1280px}}@media (min-width: 1536px){.container{max-width:1536px}}.visible{visibility:visible}.collapse{visibility:collapse}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.right-0{right:0}.top-0{top:0}.z-10{z-index:10}.mx-auto{margin-left:auto;margin-right:auto}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.ml-2{margin-left:.5rem}.ml-6{margin-left:1.5rem}.ml-auto{margin-left:auto}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.line-clamp-2{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2}.block{display:block}.flex{display:flex}.hidden{display:none}.h-12{height:3rem}.h-4{height:1rem}.h-full{height:100%}.h-screen{height:100vh}.w-1{width:.25rem}.w-12{width:3rem}.w-4{width:1rem}.w-full{width:100%}.min-w-0{min-width:0px}.max-w-4xl{max-width:56rem}.flex-1{flex:1 1 0%}.flex-shrink-0{flex-shrink:0}.rotate-90{--tw-rotate: 90deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.cursor-col-resize{cursor:col-resize}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem * var(--tw-space-y-reverse))}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.rounded{border-radius:.25rem}.rounded-lg{border-radius:.5rem}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-l{border-left-width:1px}.border-l-2{border-left-width:2px}.border-r{border-right-width:1px}.border-blue-400{--tw-border-opacity: 1;border-color:rgb(96 165 250 / var(--tw-border-opacity, 1))}.border-blue-500{--tw-border-opacity: 1;border-color:rgb(59 130 246 / var(--tw-border-opacity, 1))}.border-gray-700{--tw-border-opacity: 1;border-color:rgb(55 65 81 / var(--tw-border-opacity, 1))}.border-transparent{border-color:transparent}.bg-blue-500{--tw-bg-opacity: 1;background-color:rgb(59 130 246 / var(--tw-bg-opacity, 1))}.bg-blue-900{--tw-bg-opacity: 1;background-color:rgb(30 58 138 / var(--tw-bg-opacity, 1))}.bg-blue-900\/50{background-color:#1e3a8a80}.bg-gray-700{--tw-bg-opacity: 1;background-color:rgb(55 65 81 / var(--tw-bg-opacity, 1))}.bg-gray-800{--tw-bg-opacity: 1;background-color:rgb(31 41 55 / var(--tw-bg-opacity, 1))}.bg-gray-900{--tw-bg-opacity: 1;background-color:rgb(17 24 39 / var(--tw-bg-opacity, 1))}.bg-gray-900\/50{background-color:#11182780}.bg-gray-900\/70{background-color:#111827b3}.bg-green-900{--tw-bg-opacity: 1;background-color:rgb(20 83 45 / var(--tw-bg-opacity, 1))}.bg-green-900\/30{background-color:#14532d4d}.bg-green-900\/50{background-color:#14532d80}.bg-purple-700{--tw-bg-opacity: 1;background-color:rgb(126 34 206 / var(--tw-bg-opacity, 1))}.bg-purple-900\/50{background-color:#581c8780}.bg-transparent{background-color:transparent}.bg-yellow-900\/30{background-color:#713f124d}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.pl-14{padding-left:3.5rem}.pl-4{padding-left:1rem}.pl-8{padding-left:2rem}.text-left{text-align:left}.text-center{text-align:center}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-\[10px\]{font-size:10px}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.leading-tight{line-height:1.25}.text-blue-200{--tw-text-opacity: 1;color:rgb(191 219 254 / var(--tw-text-opacity, 1))}.text-blue-300{--tw-text-opacity: 1;color:rgb(147 197 253 / var(--tw-text-opacity, 1))}.text-gray-200{--tw-text-opacity: 1;color:rgb(229 231 235 / var(--tw-text-opacity, 1))}.text-gray-300{--tw-text-opacity: 1;color:rgb(209 213 219 / var(--tw-text-opacity, 1))}.text-gray-400{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity, 1))}.text-gray-500{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity, 1))}.text-gray-600{--tw-text-opacity: 1;color:rgb(75 85 99 / var(--tw-text-opacity, 1))}.text-green-200{--tw-text-opacity: 1;color:rgb(187 247 208 / var(--tw-text-opacity, 1))}.text-green-300{--tw-text-opacity: 1;color:rgb(134 239 172 / var(--tw-text-opacity, 1))}.text-green-400{--tw-text-opacity: 1;color:rgb(74 222 128 / var(--tw-text-opacity, 1))}.text-purple-100{--tw-text-opacity: 1;color:rgb(243 232 255 / var(--tw-text-opacity, 1))}.text-purple-300{--tw-text-opacity: 1;color:rgb(216 180 254 / var(--tw-text-opacity, 1))}.text-red-400{--tw-text-opacity: 1;color:rgb(248 113 113 / var(--tw-text-opacity, 1))}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.text-yellow-400{--tw-text-opacity: 1;color:rgb(250 204 21 / var(--tw-text-opacity, 1))}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-200{transition-duration:.2s}:root{font-family:Inter,system-ui,Avenir,Helvetica,Arial,sans-serif;line-height:1.5;font-weight:400;color-scheme:light dark;color:#ffffffde;background-color:#242424;font-synthesis:none;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}body{margin:0;display:flex;place-items:center;min-width:320px;min-height:100vh}#root{width:100%;min-height:100vh}*{scrollbar-width:thin;scrollbar-color:#4B5563 #1F2937}*::-webkit-scrollbar{width:8px;height:8px}*::-webkit-scrollbar-track{background:#1f2937}*::-webkit-scrollbar-thumb{background-color:#4b5563;border-radius:4px}*::-webkit-scrollbar-thumb:hover{background-color:#6b7280}.hover\:bg-gray-700:hover{--tw-bg-opacity: 1;background-color:rgb(55 65 81 / var(--tw-bg-opacity, 1))}.hover\:bg-gray-700\/50:hover{background-color:#37415180}.hover\:bg-gray-800:hover{--tw-bg-opacity: 1;background-color:rgb(31 41 55 / var(--tw-bg-opacity, 1))}.hover\:bg-purple-600:hover{--tw-bg-opacity: 1;background-color:rgb(147 51 234 / var(--tw-bg-opacity, 1))}.hover\:text-gray-200:hover{--tw-text-opacity: 1;color:rgb(229 231 235 / var(--tw-text-opacity, 1))}.group:hover .group-hover\:bg-blue-500{--tw-bg-opacity: 1;background-color:rgb(59 130 246 / var(--tw-bg-opacity, 1))}
|