tokenrace 0.1.13 → 0.1.15
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/index.html
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>tokenrace</title>
|
|
7
|
-
<script type="module" crossorigin src="/assets/index-
|
|
8
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
7
|
+
<script type="module" crossorigin src="/assets/index-Cy4B5yrR.js"></script>
|
|
8
|
+
<link rel="stylesheet" crossorigin href="/assets/index-Bfrxw7xK.css">
|
|
9
9
|
</head>
|
|
10
10
|
<body>
|
|
11
11
|
<div id="root"></div>
|
package/package.json
CHANGED
package/src/store.js
CHANGED
|
@@ -554,17 +554,29 @@ export function getSummary(from) {
|
|
|
554
554
|
let activeTimeMs = 0
|
|
555
555
|
const sessionSet = new Set()
|
|
556
556
|
|
|
557
|
+
// Coste desde timeseries: filtrado por timestamp exacto, sin doble conteo
|
|
558
|
+
const sessionsWithCost = new Set()
|
|
559
|
+
for (const point of state.timeseries.get('claude_code.cost') ?? []) {
|
|
560
|
+
if (point.ts < minTs) continue
|
|
561
|
+
const sid = point.labels['session.id']
|
|
562
|
+
if (sid && state.ignoredSessions.has(sid)) continue
|
|
563
|
+
cost += point.value
|
|
564
|
+
if (sid) sessionsWithCost.add(sid)
|
|
565
|
+
}
|
|
566
|
+
|
|
557
567
|
for (const session of state.sessions.values()) {
|
|
558
568
|
if (state.ignoredSessions.has(session.sessionId)) continue
|
|
559
569
|
if (session.lastSeen < minTs) continue
|
|
560
570
|
tokensInput += session.tokensInput
|
|
561
571
|
tokensOutput += session.tokensOutput
|
|
562
572
|
tokensCache += session.tokensCache
|
|
563
|
-
cost += session.cost > 0
|
|
564
|
-
? session.cost
|
|
565
|
-
: estimateCost(session.model, session.tokensInput, session.tokensOutput, session.tokensCache)
|
|
566
573
|
activeTimeMs += session.durationActiveMs
|
|
567
574
|
sessionSet.add(session.sessionId)
|
|
575
|
+
|
|
576
|
+
// Estimar solo si no llegaron métricas de coste reales para esta sesión
|
|
577
|
+
if (!sessionsWithCost.has(session.sessionId)) {
|
|
578
|
+
cost += estimateCost(session.model, session.tokensInput, session.tokensOutput, session.tokensCache)
|
|
579
|
+
}
|
|
568
580
|
}
|
|
569
581
|
|
|
570
582
|
// Commits, PRs y líneas desde timeseries
|
|
@@ -650,20 +662,37 @@ export function getProjects(from) {
|
|
|
650
662
|
}
|
|
651
663
|
}
|
|
652
664
|
|
|
665
|
+
// Coste por proyecto desde timeseries (filtrado por timestamp)
|
|
666
|
+
const projectCostTs = new Map() // projectName → cost acumulado en timeseries
|
|
667
|
+
const sessionHasCost = new Set() // sessionIds con coste real en el rango
|
|
668
|
+
|
|
669
|
+
for (const point of state.timeseries.get('claude_code.cost') ?? []) {
|
|
670
|
+
if (point.ts < minTs) continue
|
|
671
|
+
const sid = point.labels['session.id']
|
|
672
|
+
if (sid && state.ignoredSessions.has(sid)) continue
|
|
673
|
+
const proj = resolveProject(sid, point.labels.project)
|
|
674
|
+
if (!proj) continue
|
|
675
|
+
projectCostTs.set(proj, (projectCostTs.get(proj) ?? 0) + point.value)
|
|
676
|
+
if (sid) sessionHasCost.add(sid)
|
|
677
|
+
}
|
|
678
|
+
|
|
653
679
|
for (const [project, proj] of state.projects.entries()) {
|
|
654
680
|
const activeSessions = new Set()
|
|
655
|
-
let cost =
|
|
681
|
+
let cost = projectCostTs.get(project) ?? 0
|
|
682
|
+
let tokensInput = 0, tokensOutput = 0, tokensCache = 0
|
|
656
683
|
|
|
657
684
|
for (const sessionId of proj.sessions) {
|
|
658
685
|
const session = state.sessions.get(sessionId)
|
|
659
686
|
if (!session || session.lastSeen < minTs) continue
|
|
660
687
|
activeSessions.add(sessionId)
|
|
661
|
-
cost += session.cost > 0
|
|
662
|
-
? session.cost
|
|
663
|
-
: estimateCost(session.model, session.tokensInput, session.tokensOutput, session.tokensCache)
|
|
664
688
|
tokensInput += session.tokensInput
|
|
665
689
|
tokensOutput += session.tokensOutput
|
|
666
690
|
tokensCache += session.tokensCache
|
|
691
|
+
|
|
692
|
+
// Estimar solo si no llegaron métricas de coste reales para esta sesión
|
|
693
|
+
if (!sessionHasCost.has(sessionId)) {
|
|
694
|
+
cost += estimateCost(session.model, session.tokensInput, session.tokensOutput, session.tokensCache)
|
|
695
|
+
}
|
|
667
696
|
}
|
|
668
697
|
|
|
669
698
|
if (activeSessions.size === 0 && minTs > 0) continue
|
|
@@ -828,14 +857,15 @@ export function saveSync() {
|
|
|
828
857
|
fs.mkdirSync(dataDir, { recursive: true, mode: 0o700 })
|
|
829
858
|
|
|
830
859
|
const data = {
|
|
831
|
-
timeseries:
|
|
832
|
-
sessions:
|
|
833
|
-
sessionMappings:
|
|
834
|
-
ignoredSessions:
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
860
|
+
timeseries: Array.from(state.timeseries.entries()),
|
|
861
|
+
sessions: Array.from(state.sessions.values()),
|
|
862
|
+
sessionMappings: Array.from(state.sessionMappings.entries()),
|
|
863
|
+
ignoredSessions: Array.from(state.ignoredSessions),
|
|
864
|
+
cumulativeValues: Array.from(state.cumulativeValues.entries()),
|
|
865
|
+
events: state.events,
|
|
866
|
+
eventIndex: state.eventIndex,
|
|
867
|
+
totalEvents: state.totalEvents,
|
|
868
|
+
startTime: state.startTime
|
|
839
869
|
}
|
|
840
870
|
|
|
841
871
|
fs.writeFileSync(dataFile, JSON.stringify(data), { mode: 0o600 })
|
|
@@ -885,6 +915,13 @@ export function loadFromDisk() {
|
|
|
885
915
|
}
|
|
886
916
|
}
|
|
887
917
|
|
|
918
|
+
// Restaurar baselines de métricas cumulativas (evita doble conteo al reiniciar)
|
|
919
|
+
if (Array.isArray(data.cumulativeValues)) {
|
|
920
|
+
for (const [k, v] of data.cumulativeValues) {
|
|
921
|
+
state.cumulativeValues.set(k, v)
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
|
|
888
925
|
// Restaurar buffer de eventos
|
|
889
926
|
if (Array.isArray(data.events)) {
|
|
890
927
|
state.events.push(...data.events)
|
|
@@ -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:JetBrains Mono,Fira Code,Menlo,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}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.right-2{right:.5rem}.top-0{top:0}.top-10{top:2.5rem}.top-2{top:.5rem}.z-40{z-index:40}.z-50{z-index:50}.mx-auto{margin-left:auto;margin-right:auto}.mb-1{margin-bottom:.25rem}.mb-2{margin-bottom:.5rem}.mb-4{margin-bottom:1rem}.ml-2{margin-left:.5rem}.ml-auto{margin-left:auto}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.inline-block{display:inline-block}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.h-1\.5{height:.375rem}.h-10{height:2.5rem}.h-2{height:.5rem}.h-48{height:12rem}.h-\[600px\]{height:600px}.h-full{height:100%}.max-h-40{max-height:10rem}.min-h-\[60vh\]{min-height:60vh}.min-h-screen{min-height:100vh}.w-2{width:.5rem}.w-20{width:5rem}.w-24{width:6rem}.w-32{width:8rem}.w-6{width:1.5rem}.w-full{width:100%}.min-w-0{min-width:0px}.max-w-screen-2xl{max-width:1536px}.max-w-xl{max-width:36rem}.flex-1{flex:1 1 0%}.shrink-0{flex-shrink:0}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}.cursor-pointer{cursor:pointer}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-6{grid-template-columns:repeat(6,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}.space-y-0\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.125rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.125rem * var(--tw-space-y-reverse))}.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-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.75rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem * 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-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.whitespace-nowrap{white-space:nowrap}.whitespace-pre{white-space:pre}.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-b-2{border-bottom-width:2px}.border-accent-green{--tw-border-opacity: 1;border-color:rgb(255 107 53 / var(--tw-border-opacity, 1))}.border-bg-border{--tw-border-opacity: 1;border-color:rgb(26 26 26 / var(--tw-border-opacity, 1))}.border-transparent{border-color:transparent}.bg-accent-green{--tw-bg-opacity: 1;background-color:rgb(255 107 53 / var(--tw-bg-opacity, 1))}.bg-accent-purple{--tw-bg-opacity: 1;background-color:rgb(168 85 247 / var(--tw-bg-opacity, 1))}.bg-accent-teal{--tw-bg-opacity: 1;background-color:rgb(0 212 170 / var(--tw-bg-opacity, 1))}.bg-accent-yellow{--tw-bg-opacity: 1;background-color:rgb(251 191 36 / var(--tw-bg-opacity, 1))}.bg-bg-base{--tw-bg-opacity: 1;background-color:rgb(20 25 31 / var(--tw-bg-opacity, 1))}.bg-bg-card,.bg-bg-subtle{--tw-bg-opacity: 1;background-color:rgb(33 38 43 / var(--tw-bg-opacity, 1))}.bg-text-muted{--tw-bg-opacity: 1;background-color:rgb(68 68 68 / var(--tw-bg-opacity, 1))}.p-1{padding:.25rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.p-8{padding:2rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.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}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.font-mono{font-family:JetBrains Mono,Fira Code,Menlo,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-4xl{font-size:2.25rem;line-height:2.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.tracking-wider{letter-spacing:.05em}.text-accent-blue{--tw-text-opacity: 1;color:rgb(77 166 255 / var(--tw-text-opacity, 1))}.text-accent-green,.text-accent-orange{--tw-text-opacity: 1;color:rgb(255 107 53 / var(--tw-text-opacity, 1))}.text-accent-purple{--tw-text-opacity: 1;color:rgb(168 85 247 / var(--tw-text-opacity, 1))}.text-accent-teal{--tw-text-opacity: 1;color:rgb(0 212 170 / var(--tw-text-opacity, 1))}.text-accent-yellow{--tw-text-opacity: 1;color:rgb(251 191 36 / var(--tw-text-opacity, 1))}.text-black{--tw-text-opacity: 1;color:rgb(0 0 0 / var(--tw-text-opacity, 1))}.text-text-muted{--tw-text-opacity: 1;color:rgb(68 68 68 / var(--tw-text-opacity, 1))}.text-text-primary{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.text-text-secondary{--tw-text-opacity: 1;color:rgb(136 136 136 / var(--tw-text-opacity, 1))}.opacity-50{opacity:.5}.opacity-60{opacity:.6}.outline-none{outline:2px solid transparent;outline-offset:2px}.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}:root{--bg-base: #14191F;--bg-card: #21262B;--bg-card-hover: #272d33;--bg-border: #1a1a1a;--bg-subtle: #21262B;--text-primary: #ffffff;--text-secondary: #888888;--text-muted: #444444;--accent-green: #ff6b35;--accent-blue: #4da6ff;--accent-purple: #a855f7;--accent-orange: #ff6b35;--accent-teal: #00d4aa;--accent-yellow: #fbbf24}*{box-sizing:border-box}html,body,#root{height:100%;margin:0;padding:0;background-color:#14191f;color:#fff}body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;-webkit-font-smoothing:antialiased}::-webkit-scrollbar{width:6px;height:6px}::-webkit-scrollbar-track{background:#0d0d0d}::-webkit-scrollbar-thumb{background:#333;border-radius:3px}::-webkit-scrollbar-thumb:hover{background:#444}.hover\:border-accent-green:hover{--tw-border-opacity: 1;border-color:rgb(255 107 53 / var(--tw-border-opacity, 1))}.hover\:bg-bg-base:hover{--tw-bg-opacity: 1;background-color:rgb(20 25 31 / var(--tw-bg-opacity, 1))}.hover\:bg-bg-card:hover{--tw-bg-opacity: 1;background-color:rgb(33 38 43 / var(--tw-bg-opacity, 1))}.hover\:bg-bg-card-hover:hover{--tw-bg-opacity: 1;background-color:rgb(39 45 51 / var(--tw-bg-opacity, 1))}.hover\:text-accent-orange:hover{--tw-text-opacity: 1;color:rgb(255 107 53 / var(--tw-text-opacity, 1))}.hover\:text-text-primary:hover{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.focus\:border-accent-green:focus{--tw-border-opacity: 1;border-color:rgb(255 107 53 / var(--tw-border-opacity, 1))}.disabled\:opacity-40:disabled{opacity:.4}@media(min-width:768px){.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}@media(min-width:1024px){.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}
|