fluxflow-cli 4.16.3 → 4.16.5
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/chunks/{ai-6RTKBZ7C.js → ai-7NAPUVPT.js} +1 -1
- package/dist/chunks/{ai-YEPKGYDE.js → ai-DA4CWJF4.js} +1 -1
- package/dist/chunks/app-ARERN5VP.js +283 -0
- package/dist/chunks/app-EFZ3IOWI.js +283 -0
- package/dist/chunks/{chunk-JABAGBQ6.js → chunk-2XZBVZAF.js} +3 -3
- package/dist/chunks/{chunk-ZKQ5PQXN.js → chunk-4VUJ7ITH.js} +1 -1
- package/dist/chunks/{chunk-5PKBYQB2.js → chunk-C6GGJJHJ.js} +157 -156
- package/dist/chunks/chunk-HNUV7OHA.js +48 -0
- package/dist/chunks/{chunk-YVFWOJHY.js → chunk-POB3KUHE.js} +1 -1
- package/dist/chunks/chunk-Q2DA5ED2.js +33 -0
- package/dist/chunks/chunk-SHTF7RHG.js +52 -0
- package/dist/chunks/{exec_command-7MOYM7IQ.js → exec_command-ERULJCYR.js} +1 -1
- package/dist/chunks/exec_command-GJFLZQRS.js +1 -0
- package/dist/chunks/{invoke-7JQ57VBG.js → invoke-J6BIGQ4S.js} +2 -2
- package/dist/chunks/{invoke-D7WH5BU6.js → invoke-WUFZZUDC.js} +2 -2
- package/dist/chunks/{invokeSync-722CH233.js → invokeSync-JINIKUNX.js} +2 -2
- package/dist/chunks/{invokeSync-WFDY5DZJ.js → invokeSync-NHNXTDRE.js} +2 -2
- package/dist/chunks/{update_file-UFCBJOW5.js → update_file-U2ZALD44.js} +1 -1
- package/dist/cli.js +1 -1
- package/dist/package.json +1 -1
- package/package.json +1 -1
- package/dist/chunks/app-CZ4F6LXZ.js +0 -279
- package/dist/chunks/app-YEFXJS5K.js +0 -279
- package/dist/chunks/chunk-M3KCPUDW.js +0 -51
- package/dist/chunks/chunk-TCBC5P3S.js +0 -1916
- package/dist/chunks/usageServer-JKD3OZQ7.js +0 -1
|
@@ -1,1916 +0,0 @@
|
|
|
1
|
-
import{n as v}from"./chunk-WKHYRBBK.js";import{c as f}from"./chunk-FDNLMLIM.js";import b from"http";import{exec as p}from"child_process";var g="data:image/png;base64,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";var m=null,n=null;function u(){let t=g;return`<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<title>FluxFlow Token Usage & Analytics Dashboard</title>
|
|
7
|
-
${t?`<link rel="icon" type="image/png" href="${t}">`:""}
|
|
8
|
-
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
9
|
-
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
10
|
-
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@300;400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet">
|
|
11
|
-
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.7/dist/chart.umd.min.js"></script>
|
|
12
|
-
<style>
|
|
13
|
-
:root {
|
|
14
|
-
--bg-base: #07090e;
|
|
15
|
-
--bg-card: rgba(16, 22, 34, 0.75);
|
|
16
|
-
--bg-card-hover: rgba(22, 30, 48, 0.85);
|
|
17
|
-
--border-subtle: rgba(255, 255, 255, 0.08);
|
|
18
|
-
--border-glow: rgba(56, 189, 248, 0.25);
|
|
19
|
-
--text-main: #f1f5f9;
|
|
20
|
-
--text-muted: #94a3b8;
|
|
21
|
-
--text-dim: #64748b;
|
|
22
|
-
--accent-cyan: #38bdf8;
|
|
23
|
-
--accent-emerald: #10b981;
|
|
24
|
-
--accent-violet: #a855f7;
|
|
25
|
-
--accent-amber: #f59e0b;
|
|
26
|
-
--accent-rose: #f43f5e;
|
|
27
|
-
--accent-blue: #3b82f6;
|
|
28
|
-
--radius-lg: 16px;
|
|
29
|
-
--radius-md: 10px;
|
|
30
|
-
--radius-sm: 6px;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
* {
|
|
34
|
-
box-sizing: border-box;
|
|
35
|
-
margin: 0;
|
|
36
|
-
padding: 0;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
body {
|
|
40
|
-
background-color: var(--bg-base);
|
|
41
|
-
color: var(--text-main);
|
|
42
|
-
font-family: 'Plus Jakarta Sans', -apple-system, BlinkMacSystemFont, sans-serif;
|
|
43
|
-
min-height: 100vh;
|
|
44
|
-
overflow-x: hidden;
|
|
45
|
-
user-select: none;
|
|
46
|
-
-webkit-user-select: none;
|
|
47
|
-
background-image:
|
|
48
|
-
radial-gradient(circle at 15% 10%, rgba(56, 189, 248, 0.08) 0%, transparent 45%),
|
|
49
|
-
radial-gradient(circle at 85% 20%, rgba(168, 85, 247, 0.08) 0%, transparent 45%),
|
|
50
|
-
radial-gradient(circle at 50% 80%, rgba(16, 185, 129, 0.06) 0%, transparent 50%);
|
|
51
|
-
background-attachment: fixed;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
input {
|
|
55
|
-
user-select: auto;
|
|
56
|
-
-webkit-user-select: auto;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
.mono {
|
|
60
|
-
font-family: 'JetBrains Mono', monospace;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
.glass-panel {
|
|
64
|
-
background: var(--bg-card);
|
|
65
|
-
backdrop-filter: blur(16px);
|
|
66
|
-
-webkit-backdrop-filter: blur(16px);
|
|
67
|
-
border: 1px solid var(--border-subtle);
|
|
68
|
-
border-radius: var(--radius-lg);
|
|
69
|
-
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.37);
|
|
70
|
-
transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
.glass-panel:hover {
|
|
74
|
-
border-color: rgba(255, 255, 255, 0.14);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
header {
|
|
78
|
-
position: sticky;
|
|
79
|
-
top: 0;
|
|
80
|
-
z-index: 100;
|
|
81
|
-
backdrop-filter: blur(24px);
|
|
82
|
-
background: rgba(7, 9, 14, 0.3);
|
|
83
|
-
border-bottom: 1px solid var(--border-subtle);
|
|
84
|
-
padding: 1rem 2rem;
|
|
85
|
-
display: flex;
|
|
86
|
-
justify-content: space-between;
|
|
87
|
-
align-items: center;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
.brand-container {
|
|
91
|
-
display: flex;
|
|
92
|
-
align-items: center;
|
|
93
|
-
gap: 12px;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
.brand-badge {
|
|
97
|
-
width: 40px;
|
|
98
|
-
height: 40px;
|
|
99
|
-
border-radius: 10px;
|
|
100
|
-
background: linear-gradient(135deg, #38bdf8 0%, #a855f7 100%);
|
|
101
|
-
display: flex;
|
|
102
|
-
align-items: center;
|
|
103
|
-
justify-content: center;
|
|
104
|
-
font-weight: 800;
|
|
105
|
-
font-size: 1.2rem;
|
|
106
|
-
color: #fff;
|
|
107
|
-
box-shadow: 0 0 20px rgba(56, 189, 248, 0.4);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
.brand-title {
|
|
111
|
-
font-size: clamp(1.1rem, 1.6vw, 1.4rem);
|
|
112
|
-
font-weight: 700;
|
|
113
|
-
letter-spacing: -0.02em;
|
|
114
|
-
background: linear-gradient(to right, #ffffff, #94a3b8);
|
|
115
|
-
-webkit-background-clip: text;
|
|
116
|
-
-webkit-text-fill-color: transparent;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
.brand-subtitle {
|
|
120
|
-
font-size: clamp(0.7rem, 0.9vw, 0.8rem);
|
|
121
|
-
color: var(--text-dim);
|
|
122
|
-
font-weight: 500;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
.header-actions {
|
|
126
|
-
display: flex;
|
|
127
|
-
align-items: center;
|
|
128
|
-
gap: 12px;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
.status-pill {
|
|
132
|
-
display: flex;
|
|
133
|
-
align-items: center;
|
|
134
|
-
gap: 8px;
|
|
135
|
-
padding: 6px 14px;
|
|
136
|
-
border-radius: 9999px;
|
|
137
|
-
background: rgba(16, 185, 129, 0.1);
|
|
138
|
-
border: 1px solid rgba(16, 185, 129, 0.25);
|
|
139
|
-
color: #34d399;
|
|
140
|
-
font-size: clamp(0.75rem, 0.9vw, 0.825rem);
|
|
141
|
-
font-weight: 600;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
.pulse-dot {
|
|
145
|
-
width: 8px;
|
|
146
|
-
height: 8px;
|
|
147
|
-
background-color: #10b981;
|
|
148
|
-
border-radius: 50%;
|
|
149
|
-
box-shadow: 0 0 8px #10b981;
|
|
150
|
-
animation: pulse-ring 2s cubic-bezier(0.215, 0.61, 0.355, 1) infinite;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
@keyframes pulse-ring {
|
|
154
|
-
0% { transform: scale(0.9); opacity: 0.8; }
|
|
155
|
-
50% { transform: scale(1.3); opacity: 1; filter: drop-shadow(0 0 4px #10b981); }
|
|
156
|
-
100% { transform: scale(0.9); opacity: 0.8; }
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
.btn {
|
|
160
|
-
background: rgba(255, 255, 255, 0.05);
|
|
161
|
-
border: 1px solid var(--border-subtle);
|
|
162
|
-
color: var(--text-main);
|
|
163
|
-
padding: 8px 16px;
|
|
164
|
-
border-radius: var(--radius-md);
|
|
165
|
-
font-size: clamp(0.78rem, 0.9vw, 0.85rem);
|
|
166
|
-
font-weight: 600;
|
|
167
|
-
cursor: pointer;
|
|
168
|
-
display: inline-flex;
|
|
169
|
-
align-items: center;
|
|
170
|
-
gap: 8px;
|
|
171
|
-
transition: all 0.2s ease;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
.btn:hover {
|
|
175
|
-
background: rgba(255, 255, 255, 0.1);
|
|
176
|
-
border-color: rgba(255, 255, 255, 0.25);
|
|
177
|
-
transform: translateY(-1px);
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
.btn-primary {
|
|
181
|
-
background: linear-gradient(135deg, #0284c7 0%, #6366f1 100%);
|
|
182
|
-
border: none;
|
|
183
|
-
color: white;
|
|
184
|
-
box-shadow: 0 4px 14px rgba(2, 132, 199, 0.3);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
.btn-primary:hover {
|
|
188
|
-
box-shadow: 0 6px 20px rgba(2, 132, 199, 0.5);
|
|
189
|
-
background: linear-gradient(135deg, #0369a1 0%, #4f46e5 100%);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
.container {
|
|
193
|
-
max-width: 1480px;
|
|
194
|
-
margin: 0 auto;
|
|
195
|
-
padding: clamp(1rem, 2.5vw, 2.5rem);
|
|
196
|
-
display: flex;
|
|
197
|
-
flex-direction: column;
|
|
198
|
-
gap: clamp(1.25rem, 2vw, 2rem);
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
.controls-bar {
|
|
202
|
-
display: flex;
|
|
203
|
-
flex-wrap: wrap;
|
|
204
|
-
justify-content: space-between;
|
|
205
|
-
align-items: center;
|
|
206
|
-
gap: 1rem;
|
|
207
|
-
padding: 1rem 1.5rem;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
.tabs-group {
|
|
211
|
-
display: flex;
|
|
212
|
-
background: rgba(0, 0, 0, 0.3);
|
|
213
|
-
border: 1px solid var(--border-subtle);
|
|
214
|
-
border-radius: var(--radius-md);
|
|
215
|
-
padding: 4px;
|
|
216
|
-
gap: 4px;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
.tab-btn {
|
|
220
|
-
background: transparent;
|
|
221
|
-
border: none;
|
|
222
|
-
color: var(--text-muted);
|
|
223
|
-
padding: 6px 16px;
|
|
224
|
-
font-size: clamp(0.78rem, 0.9vw, 0.85rem);
|
|
225
|
-
font-weight: 600;
|
|
226
|
-
border-radius: var(--radius-sm);
|
|
227
|
-
cursor: pointer;
|
|
228
|
-
transition: all 0.2s ease;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
.tab-btn.active {
|
|
232
|
-
background: rgba(56, 189, 248, 0.15);
|
|
233
|
-
color: var(--accent-cyan);
|
|
234
|
-
border: 1px solid rgba(56, 189, 248, 0.3);
|
|
235
|
-
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
.metrics-grid {
|
|
239
|
-
display: grid;
|
|
240
|
-
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
|
241
|
-
gap: 1.25rem;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
.metric-card {
|
|
245
|
-
padding: clamp(1.15rem, 1.8vw, 1.6rem);
|
|
246
|
-
position: relative;
|
|
247
|
-
overflow: hidden;
|
|
248
|
-
display: flex;
|
|
249
|
-
flex-direction: column;
|
|
250
|
-
justify-content: space-between;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
.metric-card::before {
|
|
254
|
-
content: '';
|
|
255
|
-
position: absolute;
|
|
256
|
-
top: 0;
|
|
257
|
-
left: 0;
|
|
258
|
-
width: 100%;
|
|
259
|
-
height: 3px;
|
|
260
|
-
background: linear-gradient(90deg, transparent, var(--card-accent, var(--accent-cyan)), transparent);
|
|
261
|
-
opacity: 0.8;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
.metric-header {
|
|
265
|
-
display: flex;
|
|
266
|
-
justify-content: space-between;
|
|
267
|
-
align-items: flex-start;
|
|
268
|
-
margin-bottom: 0.75rem;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
.metric-label {
|
|
272
|
-
font-size: clamp(0.75rem, 0.9vw, 0.825rem);
|
|
273
|
-
font-weight: 600;
|
|
274
|
-
text-transform: uppercase;
|
|
275
|
-
letter-spacing: 0.05em;
|
|
276
|
-
color: var(--text-muted);
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
.metric-icon {
|
|
280
|
-
width: 32px;
|
|
281
|
-
height: 32px;
|
|
282
|
-
border-radius: 8px;
|
|
283
|
-
background: rgba(255, 255, 255, 0.04);
|
|
284
|
-
display: flex;
|
|
285
|
-
align-items: center;
|
|
286
|
-
justify-content: center;
|
|
287
|
-
font-size: 1.1rem;
|
|
288
|
-
color: var(--card-accent, var(--accent-cyan));
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
.metric-value {
|
|
292
|
-
font-size: clamp(1.2rem, 1.65vw, 1.65rem);
|
|
293
|
-
font-weight: 800;
|
|
294
|
-
letter-spacing: -0.02em;
|
|
295
|
-
color: var(--text-main);
|
|
296
|
-
margin-bottom: 0.4rem;
|
|
297
|
-
line-height: 1.2;
|
|
298
|
-
white-space: nowrap;
|
|
299
|
-
overflow: hidden;
|
|
300
|
-
text-overflow: ellipsis;
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
.metric-sub {
|
|
304
|
-
font-size: clamp(0.725rem, 0.85vw, 0.8rem);
|
|
305
|
-
color: var(--text-dim);
|
|
306
|
-
display: flex;
|
|
307
|
-
align-items: center;
|
|
308
|
-
gap: 6px;
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
.badge-positive {
|
|
312
|
-
color: var(--accent-emerald);
|
|
313
|
-
background: rgba(16, 185, 129, 0.1);
|
|
314
|
-
padding: 2px 6px;
|
|
315
|
-
border-radius: 4px;
|
|
316
|
-
font-weight: 600;
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
.badge-info {
|
|
320
|
-
color: var(--accent-cyan);
|
|
321
|
-
background: rgba(56, 189, 248, 0.1);
|
|
322
|
-
padding: 2px 6px;
|
|
323
|
-
border-radius: 4px;
|
|
324
|
-
font-weight: 600;
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
.chart-row {
|
|
328
|
-
display: grid;
|
|
329
|
-
grid-template-columns: 2fr 1fr;
|
|
330
|
-
gap: 1.5rem;
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
@media (max-width: 1024px) {
|
|
334
|
-
.chart-row {
|
|
335
|
-
grid-template-columns: 1fr;
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
.chart-box {
|
|
340
|
-
padding: clamp(1.15rem, 1.8vw, 1.6rem);
|
|
341
|
-
display: flex;
|
|
342
|
-
flex-direction: column;
|
|
343
|
-
gap: 1rem;
|
|
344
|
-
min-height: 450px;
|
|
345
|
-
max-height: 70vh;
|
|
346
|
-
overflow: hidden;
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
.chart-header {
|
|
350
|
-
display: flex;
|
|
351
|
-
justify-content: space-between;
|
|
352
|
-
align-items: center;
|
|
353
|
-
flex-wrap: wrap;
|
|
354
|
-
gap: 0.5rem;
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
.chart-title-group {
|
|
358
|
-
display: flex;
|
|
359
|
-
flex-direction: column;
|
|
360
|
-
gap: 4px;
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
.chart-title {
|
|
364
|
-
font-size: clamp(1rem, 1.3vw, 1.2rem);
|
|
365
|
-
font-weight: 700;
|
|
366
|
-
color: var(--text-main);
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
.chart-subtitle {
|
|
370
|
-
font-size: clamp(0.75rem, 0.9vw, 0.85rem);
|
|
371
|
-
color: var(--text-muted);
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
.chart-controls {
|
|
375
|
-
display: flex;
|
|
376
|
-
gap: 6px;
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
.chart-btn {
|
|
380
|
-
background: rgba(255, 255, 255, 0.05);
|
|
381
|
-
border: 1px solid var(--border-subtle);
|
|
382
|
-
color: var(--text-muted);
|
|
383
|
-
padding: 4px 10px;
|
|
384
|
-
border-radius: var(--radius-sm);
|
|
385
|
-
font-size: 0.75rem;
|
|
386
|
-
font-weight: 600;
|
|
387
|
-
cursor: pointer;
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
.chart-btn.active {
|
|
391
|
-
background: rgba(56, 189, 248, 0.2);
|
|
392
|
-
color: var(--accent-cyan);
|
|
393
|
-
border-color: rgba(56, 189, 248, 0.4);
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
.canvas-wrapper {
|
|
397
|
-
position: relative;
|
|
398
|
-
flex: 1;
|
|
399
|
-
width: 100%;
|
|
400
|
-
min-height: 280px;
|
|
401
|
-
max-height: calc(70vh - 90px);
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
.pie-canvas-wrapper {
|
|
405
|
-
position: relative;
|
|
406
|
-
width: 100%;
|
|
407
|
-
height: 210px;
|
|
408
|
-
max-height: 220px;
|
|
409
|
-
min-height: 180px;
|
|
410
|
-
display: flex;
|
|
411
|
-
align-items: center;
|
|
412
|
-
justify-content: center;
|
|
413
|
-
flex-shrink: 0;
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
.secondary-grid {
|
|
417
|
-
display: grid;
|
|
418
|
-
grid-template-columns: repeat(auto-fit, minmax(340px, 1fr));
|
|
419
|
-
gap: 1.5rem;
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
.budget-box {
|
|
423
|
-
padding: clamp(1.15rem, 1.8vw, 1.6rem);
|
|
424
|
-
display: flex;
|
|
425
|
-
flex-direction: column;
|
|
426
|
-
gap: 1rem;
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
.budget-grid {
|
|
430
|
-
display: grid;
|
|
431
|
-
grid-template-columns: repeat(auto-fit, minmax(360px, 1fr));
|
|
432
|
-
gap: 1.25rem;
|
|
433
|
-
margin-top: 0.25rem;
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
.budget-card {
|
|
437
|
-
background: rgba(255, 255, 255, 0.02);
|
|
438
|
-
border: 1px solid var(--border-subtle);
|
|
439
|
-
border-radius: var(--radius-md);
|
|
440
|
-
padding: 1.25rem;
|
|
441
|
-
display: flex;
|
|
442
|
-
flex-direction: column;
|
|
443
|
-
gap: 0.9rem;
|
|
444
|
-
transition: all 0.2s ease;
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
.budget-card:hover {
|
|
448
|
-
border-color: rgba(255, 255, 255, 0.12);
|
|
449
|
-
background: rgba(255, 255, 255, 0.035);
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
.budget-card-header {
|
|
453
|
-
display: flex;
|
|
454
|
-
justify-content: space-between;
|
|
455
|
-
align-items: center;
|
|
456
|
-
padding-bottom: 0.4rem;
|
|
457
|
-
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
.budget-group-title {
|
|
461
|
-
font-size: 0.95rem;
|
|
462
|
-
font-weight: 700;
|
|
463
|
-
color: var(--text-main);
|
|
464
|
-
display: flex;
|
|
465
|
-
align-items: center;
|
|
466
|
-
gap: 7px;
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
.budget-sub-block {
|
|
470
|
-
display: flex;
|
|
471
|
-
flex-direction: column;
|
|
472
|
-
gap: 0.45rem;
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
.budget-sub-header {
|
|
476
|
-
display: flex;
|
|
477
|
-
justify-content: space-between;
|
|
478
|
-
align-items: center;
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
.budget-sub-title {
|
|
482
|
-
font-size: 0.8rem;
|
|
483
|
-
font-weight: 600;
|
|
484
|
-
color: var(--text-muted);
|
|
485
|
-
display: flex;
|
|
486
|
-
align-items: center;
|
|
487
|
-
gap: 5px;
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
.budget-sub-divider {
|
|
491
|
-
height: 1px;
|
|
492
|
-
background: rgba(255, 255, 255, 0.04);
|
|
493
|
-
margin: 0.15rem 0;
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
.budget-title {
|
|
497
|
-
font-size: 0.85rem;
|
|
498
|
-
font-weight: 700;
|
|
499
|
-
color: var(--text-main);
|
|
500
|
-
display: flex;
|
|
501
|
-
align-items: center;
|
|
502
|
-
gap: 6px;
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
.budget-tag {
|
|
506
|
-
font-size: 0.7rem;
|
|
507
|
-
padding: 2px 8px;
|
|
508
|
-
border-radius: 999px;
|
|
509
|
-
font-weight: 600;
|
|
510
|
-
background: rgba(255, 255, 255, 0.05);
|
|
511
|
-
color: var(--text-muted);
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
.budget-values {
|
|
515
|
-
display: flex;
|
|
516
|
-
justify-content: space-between;
|
|
517
|
-
align-items: baseline;
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
.budget-cur {
|
|
521
|
-
font-size: clamp(1.2rem, 1.6vw, 1.5rem);
|
|
522
|
-
font-weight: 800;
|
|
523
|
-
color: var(--text-main);
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
.budget-max {
|
|
527
|
-
font-size: 0.8rem;
|
|
528
|
-
color: var(--text-dim);
|
|
529
|
-
font-weight: 500;
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
.budget-progress-track {
|
|
533
|
-
width: 100%;
|
|
534
|
-
height: 8px;
|
|
535
|
-
background: rgba(255, 255, 255, 0.05);
|
|
536
|
-
border-radius: 999px;
|
|
537
|
-
overflow: hidden;
|
|
538
|
-
position: relative;
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
.budget-progress-fill {
|
|
542
|
-
height: 100%;
|
|
543
|
-
border-radius: 999px;
|
|
544
|
-
transition: width 0.6s cubic-bezier(0.16, 1, 0.3, 1);
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
.budget-footer {
|
|
548
|
-
display: flex;
|
|
549
|
-
justify-content: space-between;
|
|
550
|
-
align-items: center;
|
|
551
|
-
font-size: 0.75rem;
|
|
552
|
-
color: var(--text-dim);
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
.table-box {
|
|
556
|
-
padding: 1.5rem;
|
|
557
|
-
overflow: hidden;
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
.table-wrapper {
|
|
561
|
-
overflow-x: auto;
|
|
562
|
-
margin-top: 1rem;
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
table {
|
|
566
|
-
width: 100%;
|
|
567
|
-
border-collapse: collapse;
|
|
568
|
-
text-align: left;
|
|
569
|
-
font-size: clamp(0.78rem, 0.9vw, 0.85rem);
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
th {
|
|
573
|
-
padding: 12px 16px;
|
|
574
|
-
background: rgba(0, 0, 0, 0.2);
|
|
575
|
-
color: var(--text-muted);
|
|
576
|
-
font-weight: 600;
|
|
577
|
-
border-bottom: 1px solid var(--border-subtle);
|
|
578
|
-
text-transform: uppercase;
|
|
579
|
-
font-size: clamp(0.68rem, 0.8vw, 0.75rem);
|
|
580
|
-
letter-spacing: 0.05em;
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
th.sortable {
|
|
584
|
-
cursor: pointer;
|
|
585
|
-
user-select: none;
|
|
586
|
-
transition: all 0.2s ease;
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
th.sortable:hover {
|
|
590
|
-
color: var(--accent-cyan);
|
|
591
|
-
background: rgba(56, 189, 248, 0.08);
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
th .sort-icon {
|
|
595
|
-
display: inline-block;
|
|
596
|
-
margin-left: 5px;
|
|
597
|
-
height: 20px;
|
|
598
|
-
width: 20px;
|
|
599
|
-
font-size: 0.75rem;
|
|
600
|
-
color: var(--text-dim);
|
|
601
|
-
transition: transform 0.15s ease, color 0.15s ease;
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
th:first-child,
|
|
605
|
-
td:first-child {
|
|
606
|
-
width: 300px;
|
|
607
|
-
min-width: 300px;
|
|
608
|
-
max-width: 300px;
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
th.col-code,
|
|
612
|
-
td.col-code {
|
|
613
|
-
width: 170px;
|
|
614
|
-
min-width: 170px;
|
|
615
|
-
white-space: nowrap;
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
td {
|
|
619
|
-
padding: 12px 16px;
|
|
620
|
-
border-bottom: 1px solid rgba(255, 255, 255, 0.04);
|
|
621
|
-
color: var(--text-main);
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
tr:hover td {
|
|
625
|
-
background: rgba(255, 255, 255, 0.02);
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
.breakdown-list {
|
|
629
|
-
display: flex;
|
|
630
|
-
flex-direction: column;
|
|
631
|
-
gap: 10px;
|
|
632
|
-
margin-top: 8px;
|
|
633
|
-
overflow-y: auto;
|
|
634
|
-
max-height: 160px;
|
|
635
|
-
padding-right: 4px;
|
|
636
|
-
}
|
|
637
|
-
|
|
638
|
-
.breakdown-item {
|
|
639
|
-
display: flex;
|
|
640
|
-
flex-direction: column;
|
|
641
|
-
gap: 6px;
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
.breakdown-header {
|
|
645
|
-
display: flex;
|
|
646
|
-
justify-content: space-between;
|
|
647
|
-
font-size: 0.825rem;
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
.progress-bar-bg {
|
|
651
|
-
width: 100%;
|
|
652
|
-
height: 6px;
|
|
653
|
-
background: rgba(255, 255, 255, 0.06);
|
|
654
|
-
border-radius: 999px;
|
|
655
|
-
overflow: hidden;
|
|
656
|
-
}
|
|
657
|
-
|
|
658
|
-
.progress-bar-fill {
|
|
659
|
-
height: 100%;
|
|
660
|
-
border-radius: 999px;
|
|
661
|
-
background: linear-gradient(90deg, var(--accent-cyan), var(--accent-violet));
|
|
662
|
-
transition: width 0.6s cubic-bezier(0.4, 0, 0.2, 1);
|
|
663
|
-
}
|
|
664
|
-
|
|
665
|
-
footer {
|
|
666
|
-
margin-top: 3rem;
|
|
667
|
-
padding: 1.5rem 2rem;
|
|
668
|
-
border-top: 1px solid var(--border-subtle);
|
|
669
|
-
text-align: center;
|
|
670
|
-
color: var(--text-dim);
|
|
671
|
-
font-size: 0.8rem;
|
|
672
|
-
display: flex;
|
|
673
|
-
justify-content: space-between;
|
|
674
|
-
align-items: center;
|
|
675
|
-
flex-wrap: wrap;
|
|
676
|
-
gap: 1rem;
|
|
677
|
-
}
|
|
678
|
-
</style>
|
|
679
|
-
</head>
|
|
680
|
-
<body>
|
|
681
|
-
<header>
|
|
682
|
-
<div class="brand-container">
|
|
683
|
-
<div class="brand-badge">
|
|
684
|
-
${t?`<img src="${t}" alt="FluxFlow" style="width: 100%; height: 100%; object-fit: cover; border-radius: 10px;" />`:"\u26A1"}
|
|
685
|
-
</div>
|
|
686
|
-
<div>
|
|
687
|
-
<div class="brand-title">FluxFlow Analytics</div>
|
|
688
|
-
<div class="brand-subtitle">Token Intelligence & Activity Monitor</div>
|
|
689
|
-
</div>
|
|
690
|
-
</div>
|
|
691
|
-
<div class="header-actions">
|
|
692
|
-
<button class="btn" id="btn-export-json" title="Export Token Usage as JSON">
|
|
693
|
-
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
|
|
694
|
-
Export JSON
|
|
695
|
-
</button>
|
|
696
|
-
<button class="btn" id="btn-export-csv" title="Export Token Usage as CSV">
|
|
697
|
-
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>
|
|
698
|
-
Export CSV
|
|
699
|
-
</button>
|
|
700
|
-
<button class="btn btn-primary" id="btn-refresh">
|
|
701
|
-
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21.5 2v6h-6M21.34 15.57a10 10 0 1 1-.57-8.38l5.67-5.67"/></svg>
|
|
702
|
-
Sync Now
|
|
703
|
-
</button>
|
|
704
|
-
</div>
|
|
705
|
-
</header>
|
|
706
|
-
|
|
707
|
-
<div class="container">
|
|
708
|
-
<!-- Controls Bar -->
|
|
709
|
-
<div class="glass-panel controls-bar">
|
|
710
|
-
<div style="display: flex; align-items: center; gap: 12px; flex-wrap: wrap;">
|
|
711
|
-
<span style="font-size: 0.85rem; font-weight: 700; color: var(--text-muted);">TIME RANGE:</span>
|
|
712
|
-
<div class="tabs-group" id="range-tabs">
|
|
713
|
-
<button class="tab-btn" data-range="today">Today</button>
|
|
714
|
-
<button class="tab-btn active" data-range="7d">Last 7 Days</button>
|
|
715
|
-
<button class="tab-btn" data-range="14d">Last 14 Days</button>
|
|
716
|
-
<button class="tab-btn" data-range="30d">Last 30 Days</button>
|
|
717
|
-
</div>
|
|
718
|
-
</div>
|
|
719
|
-
<div style="display: flex; align-items: center; gap: 12px;">
|
|
720
|
-
<label style="font-size: 0.8rem; color: var(--text-dim); display: flex; align-items: center; gap: 6px; cursor: pointer;">
|
|
721
|
-
<input type="checkbox" id="auto-refresh-check" style="accent-color: var(--accent-cyan);">
|
|
722
|
-
Auto-Sync (60s)
|
|
723
|
-
</label>
|
|
724
|
-
<span style="font-size: 0.75rem; color: var(--text-dim);" id="last-sync-time">Last synced: Just now</span>
|
|
725
|
-
</div>
|
|
726
|
-
</div>
|
|
727
|
-
|
|
728
|
-
<!-- Metrics Cards -->
|
|
729
|
-
<div class="metrics-grid">
|
|
730
|
-
<div class="glass-panel metric-card" style="--card-accent: var(--accent-cyan);">
|
|
731
|
-
<div class="metric-header">
|
|
732
|
-
<span class="metric-label">Total Tokens</span>
|
|
733
|
-
<div class="metric-icon">\u2726</div>
|
|
734
|
-
</div>
|
|
735
|
-
<div class="metric-value mono" id="m-total-tokens">0</div>
|
|
736
|
-
<div class="metric-sub">
|
|
737
|
-
<span class="badge-info" id="m-tokens-detail">0 In / 0 Out</span>
|
|
738
|
-
</div>
|
|
739
|
-
</div>
|
|
740
|
-
|
|
741
|
-
<div class="glass-panel metric-card" style="--card-accent: var(--accent-emerald);">
|
|
742
|
-
<div class="metric-header">
|
|
743
|
-
<span class="metric-label">Cache Ratio</span>
|
|
744
|
-
<div class="metric-icon">\u26A1</div>
|
|
745
|
-
</div>
|
|
746
|
-
<div class="metric-value mono" id="m-cached-tokens">0%</div>
|
|
747
|
-
<div class="metric-sub">
|
|
748
|
-
<span class="badge-positive" id="m-cached-saved">0 cached tokens</span>
|
|
749
|
-
</div>
|
|
750
|
-
</div>
|
|
751
|
-
|
|
752
|
-
<div class="glass-panel metric-card" style="--card-accent: var(--accent-amber);">
|
|
753
|
-
<div class="metric-header">
|
|
754
|
-
<span class="metric-label">API Calls</span>
|
|
755
|
-
<div class="metric-icon">\u2699\uFE0F</div>
|
|
756
|
-
</div>
|
|
757
|
-
<div class="metric-value mono" id="m-total-requests">0</div>
|
|
758
|
-
<div class="metric-sub">
|
|
759
|
-
<span id="m-requests-sub">0 Agent / 0 Sub-agents</span>
|
|
760
|
-
</div>
|
|
761
|
-
</div>
|
|
762
|
-
|
|
763
|
-
<div class="glass-panel metric-card" style="--card-accent: var(--accent-rose);">
|
|
764
|
-
<div class="metric-header">
|
|
765
|
-
<span class="metric-label">Tool Reliability</span>
|
|
766
|
-
<div class="metric-icon">\u{1F6E0}\uFE0F</div>
|
|
767
|
-
</div>
|
|
768
|
-
<div class="metric-value mono" id="m-tool-rate">100%</div>
|
|
769
|
-
<div class="metric-sub">
|
|
770
|
-
<span id="m-tool-sub">0 ok / 0 err / 0 denied</span>
|
|
771
|
-
</div>
|
|
772
|
-
</div>
|
|
773
|
-
|
|
774
|
-
<div class="glass-panel metric-card" style="--card-accent: var(--accent-blue);">
|
|
775
|
-
<div class="metric-header">
|
|
776
|
-
<span class="metric-label">Code Impact</span>
|
|
777
|
-
<div class="metric-icon">\u{1F4DD}</div>
|
|
778
|
-
</div>
|
|
779
|
-
<div class="metric-value mono" id="m-code-delta">+0 / -0</div>
|
|
780
|
-
<div class="metric-sub">
|
|
781
|
-
<span id="m-code-net">Net: 0 lines</span>
|
|
782
|
-
</div>
|
|
783
|
-
</div>
|
|
784
|
-
</div>
|
|
785
|
-
|
|
786
|
-
<!-- Budget & Quota Tracking -->
|
|
787
|
-
<div class="glass-panel budget-box" id="budget-section">
|
|
788
|
-
<div class="chart-header">
|
|
789
|
-
<div class="chart-title-group">
|
|
790
|
-
<div class="chart-title" style="display: flex; align-items: center; gap: 8px;">
|
|
791
|
-
<span>\u{1F4B0}</span>
|
|
792
|
-
<span>Budget & Quota Tracking</span>
|
|
793
|
-
<span id="budget-tier-pill" style="display: none; font-size: 0.7rem; margin-left: 4px;"></span>
|
|
794
|
-
</div>
|
|
795
|
-
<div class="chart-subtitle" id="budget-subtitle">Real-time spend against configured daily and monthly token limits</div>
|
|
796
|
-
</div>
|
|
797
|
-
<div id="budget-reset-badge" style="font-size: 0.75rem; color: var(--text-muted); background: rgba(0,0,0,0.3); padding: 4px 10px; border-radius: var(--radius-sm); border: 1px solid var(--border-subtle);">
|
|
798
|
-
Reset Mode: Daily
|
|
799
|
-
</div>
|
|
800
|
-
</div>
|
|
801
|
-
<div class="budget-grid" id="budget-grid-cards">
|
|
802
|
-
<!-- Dynamically populated by JS -->
|
|
803
|
-
</div>
|
|
804
|
-
</div>
|
|
805
|
-
|
|
806
|
-
<!-- Main Chart Row -->
|
|
807
|
-
<div class="chart-row">
|
|
808
|
-
<!-- Timeline Chart -->
|
|
809
|
-
<div class="glass-panel chart-box">
|
|
810
|
-
<div class="chart-header">
|
|
811
|
-
<div class="chart-title-group">
|
|
812
|
-
<div class="chart-title">Token Consumption Trends</div>
|
|
813
|
-
<div class="chart-subtitle">Prompt (Input), Candidate (Output), and Cached tokens breakdown</div>
|
|
814
|
-
</div>
|
|
815
|
-
<div class="chart-controls">
|
|
816
|
-
<button class="chart-btn active" data-chart-type="bar">Stacked Bar</button>
|
|
817
|
-
<button class="chart-btn" data-chart-type="line">Area Chart</button>
|
|
818
|
-
</div>
|
|
819
|
-
</div>
|
|
820
|
-
<div class="canvas-wrapper">
|
|
821
|
-
<canvas id="timelineChart"></canvas>
|
|
822
|
-
</div>
|
|
823
|
-
</div>
|
|
824
|
-
|
|
825
|
-
<!-- Provider / Model Share -->
|
|
826
|
-
<div class="glass-panel chart-box">
|
|
827
|
-
<div class="chart-header">
|
|
828
|
-
<div class="chart-title-group">
|
|
829
|
-
<div class="chart-title">Token Share by Provider</div>
|
|
830
|
-
<div class="chart-subtitle">Distribution of tokens across Inference Providers</div>
|
|
831
|
-
</div>
|
|
832
|
-
</div>
|
|
833
|
-
<div class="pie-canvas-wrapper">
|
|
834
|
-
<canvas id="providerPieChart"></canvas>
|
|
835
|
-
</div>
|
|
836
|
-
<div class="breakdown-list" id="provider-breakdown-list"></div>
|
|
837
|
-
</div>
|
|
838
|
-
</div>
|
|
839
|
-
|
|
840
|
-
<!-- Secondary Charts -->
|
|
841
|
-
<div class="secondary-grid">
|
|
842
|
-
<!-- Tool Execution Health -->
|
|
843
|
-
<div class="glass-panel chart-box">
|
|
844
|
-
<div class="chart-header">
|
|
845
|
-
<div class="chart-title-group">
|
|
846
|
-
<div class="chart-title">Tool Executions & Reliability</div>
|
|
847
|
-
<div class="chart-subtitle">Success, Error & Permission Denial breakdown</div>
|
|
848
|
-
</div>
|
|
849
|
-
</div>
|
|
850
|
-
<div class="canvas-wrapper">
|
|
851
|
-
<canvas id="toolChart"></canvas>
|
|
852
|
-
</div>
|
|
853
|
-
</div>
|
|
854
|
-
|
|
855
|
-
<!-- Code Velocity -->
|
|
856
|
-
<div class="glass-panel chart-box">
|
|
857
|
-
<div class="chart-header">
|
|
858
|
-
<div class="chart-title-group">
|
|
859
|
-
<div class="chart-title">Code Modification Volume</div>
|
|
860
|
-
<div class="chart-subtitle">Lines of code written (+) vs deleted (-) by agents</div>
|
|
861
|
-
</div>
|
|
862
|
-
</div>
|
|
863
|
-
<div class="canvas-wrapper">
|
|
864
|
-
<canvas id="codeChart"></canvas>
|
|
865
|
-
</div>
|
|
866
|
-
</div>
|
|
867
|
-
</div>
|
|
868
|
-
|
|
869
|
-
<!-- Detailed Breakdown Table -->
|
|
870
|
-
<div class="glass-panel table-box">
|
|
871
|
-
<div class="chart-header">
|
|
872
|
-
<div class="chart-title-group">
|
|
873
|
-
<div class="chart-title" id="table-view-title">Daily Detailed Token Records</div>
|
|
874
|
-
<div class="chart-subtitle" id="table-view-subtitle">Itemized log of daily token counts, cache ratios, and tool operations</div>
|
|
875
|
-
</div>
|
|
876
|
-
<div style="display: flex; align-items: center; gap: 10px; flex-wrap: wrap;">
|
|
877
|
-
<div class="tabs-group" id="table-mode-tabs">
|
|
878
|
-
<button class="tab-btn active" data-table-mode="daily">Daily Records</button>
|
|
879
|
-
<button class="tab-btn" data-table-mode="models">Model Stats</button>
|
|
880
|
-
</div>
|
|
881
|
-
<input type="text" id="table-search" placeholder="Search..." style="background: rgba(0,0,0,0.3); border: 1px solid var(--border-subtle); color: var(--text-main); padding: 6px 12px; border-radius: var(--radius-sm); font-size: 0.8rem; outline: none;">
|
|
882
|
-
</div>
|
|
883
|
-
</div>
|
|
884
|
-
<div class="table-wrapper">
|
|
885
|
-
<table id="usage-table">
|
|
886
|
-
<thead id="usage-table-head">
|
|
887
|
-
<!-- Populated by JS -->
|
|
888
|
-
</thead>
|
|
889
|
-
<tbody id="usage-table-body">
|
|
890
|
-
<!-- Populated by JS -->
|
|
891
|
-
</tbody>
|
|
892
|
-
</table>
|
|
893
|
-
</div>
|
|
894
|
-
</div>
|
|
895
|
-
</div>
|
|
896
|
-
|
|
897
|
-
<footer>
|
|
898
|
-
<div>FluxFlow CLI \u2022 High-Fidelity Agentic Terminal</div>
|
|
899
|
-
<div>Data stored locally in ${f.replaceAll("\\\\","/").replaceAll("\\","/")}</div>
|
|
900
|
-
</footer>
|
|
901
|
-
|
|
902
|
-
<script>
|
|
903
|
-
let rawData = null;
|
|
904
|
-
let activeRange = '7d';
|
|
905
|
-
let chartType = 'bar';
|
|
906
|
-
let tableMode = 'daily';
|
|
907
|
-
let dailySortField = 'date';
|
|
908
|
-
let dailySortOrder = 'desc';
|
|
909
|
-
let modelSortField = 'tokens';
|
|
910
|
-
let modelSortOrder = 'desc';
|
|
911
|
-
let timelineChart = null;
|
|
912
|
-
let providerPieChart = null;
|
|
913
|
-
let toolChart = null;
|
|
914
|
-
let codeChart = null;
|
|
915
|
-
|
|
916
|
-
function formatNumber(num) {
|
|
917
|
-
if (num === undefined || num === null) return '0';
|
|
918
|
-
if (num >= 1_000_000) return (num / 1_000_000).toFixed(2) + 'M';
|
|
919
|
-
if (num >= 1_000) return (num / 1_000).toFixed(1) + 'k';
|
|
920
|
-
return num.toLocaleString();
|
|
921
|
-
}
|
|
922
|
-
|
|
923
|
-
function filterTimelineByRange(timeline, range) {
|
|
924
|
-
if (!timeline || timeline.length === 0) return [];
|
|
925
|
-
const todayStr = rawData.currentDate || new Date().toISOString().split('T')[0];
|
|
926
|
-
const todayTime = new Date(todayStr).getTime();
|
|
927
|
-
|
|
928
|
-
let days = 7;
|
|
929
|
-
if (range === 'today') days = 1;
|
|
930
|
-
else if (range === '7d') days = 7;
|
|
931
|
-
else if (range === '14d') days = 14;
|
|
932
|
-
else if (range === '30d') days = 30;
|
|
933
|
-
else days = 30;
|
|
934
|
-
|
|
935
|
-
const cutoff = todayTime - (days - 1) * 24 * 60 * 60 * 1000;
|
|
936
|
-
return timeline.filter(item => {
|
|
937
|
-
const itemTime = new Date(item.date).getTime();
|
|
938
|
-
return itemTime >= cutoff;
|
|
939
|
-
});
|
|
940
|
-
}
|
|
941
|
-
|
|
942
|
-
async function fetchData() {
|
|
943
|
-
try {
|
|
944
|
-
const res = await fetch('/api/usage');
|
|
945
|
-
rawData = await res.json();
|
|
946
|
-
renderDashboard();
|
|
947
|
-
document.getElementById('last-sync-time').textContent = 'Last synced: ' + new Date().toLocaleTimeString();
|
|
948
|
-
} catch (err) {
|
|
949
|
-
console.error('Failed to sync usage data:', err);
|
|
950
|
-
}
|
|
951
|
-
}
|
|
952
|
-
|
|
953
|
-
function renderDashboard() {
|
|
954
|
-
if (!rawData || !rawData.timeline) return;
|
|
955
|
-
|
|
956
|
-
const filtered = filterTimelineByRange(rawData.timeline, activeRange);
|
|
957
|
-
|
|
958
|
-
let totalTokens = 0;
|
|
959
|
-
let cachedTokens = 0;
|
|
960
|
-
let candidateTokens = 0;
|
|
961
|
-
let promptTokens = 0;
|
|
962
|
-
let agentRequests = 0;
|
|
963
|
-
let bgRequests = 0;
|
|
964
|
-
let searchRequests = 0;
|
|
965
|
-
let toolSuccess = 0;
|
|
966
|
-
let toolFailure = 0;
|
|
967
|
-
let toolDenied = 0;
|
|
968
|
-
let linesAdded = 0;
|
|
969
|
-
let linesRemoved = 0;
|
|
970
|
-
|
|
971
|
-
const providerTokens = {};
|
|
972
|
-
const modelTokens = {};
|
|
973
|
-
|
|
974
|
-
filtered.forEach(item => {
|
|
975
|
-
totalTokens += item.tokens || 0;
|
|
976
|
-
cachedTokens += item.cachedTokens || 0;
|
|
977
|
-
candidateTokens += item.candidateTokens || 0;
|
|
978
|
-
promptTokens += item.promptTokens || 0;
|
|
979
|
-
agentRequests += item.agent || 0;
|
|
980
|
-
bgRequests += item.background || 0;
|
|
981
|
-
searchRequests += item.search || 0;
|
|
982
|
-
toolSuccess += item.toolSuccess || 0;
|
|
983
|
-
toolFailure += item.toolFailure || 0;
|
|
984
|
-
toolDenied += item.toolDenied || 0;
|
|
985
|
-
linesAdded += item.linesAdded || 0;
|
|
986
|
-
linesRemoved += item.linesRemoved || 0;
|
|
987
|
-
|
|
988
|
-
const models = item.models || {};
|
|
989
|
-
for (const prov in models) {
|
|
990
|
-
providerTokens[prov] = (providerTokens[prov] || 0);
|
|
991
|
-
for (const m in models[prov]) {
|
|
992
|
-
const mTok = models[prov][m]?.tokens || 0;
|
|
993
|
-
providerTokens[prov] += mTok;
|
|
994
|
-
modelTokens[m] = (modelTokens[m] || 0) + mTok;
|
|
995
|
-
}
|
|
996
|
-
}
|
|
997
|
-
});
|
|
998
|
-
|
|
999
|
-
document.getElementById('m-total-tokens').textContent = formatNumber(totalTokens);
|
|
1000
|
-
document.getElementById('m-tokens-detail').textContent = \`\${formatNumber(promptTokens)} In / \${formatNumber(candidateTokens)} Out\`;
|
|
1001
|
-
|
|
1002
|
-
const cachePercent = promptTokens > 0 ? ((cachedTokens / promptTokens) * 100).toFixed(1) : 0;
|
|
1003
|
-
document.getElementById('m-cached-tokens').textContent = cachePercent + '%';
|
|
1004
|
-
document.getElementById('m-cached-saved').textContent = \`\${formatNumber(cachedTokens)} / \${formatNumber(promptTokens)} Cached\`;
|
|
1005
|
-
|
|
1006
|
-
const totalReq = agentRequests + bgRequests + searchRequests;
|
|
1007
|
-
document.getElementById('m-total-requests').textContent = totalReq.toLocaleString();
|
|
1008
|
-
document.getElementById('m-requests-sub').textContent = \`\${agentRequests} Agent / \${bgRequests} Background\`;
|
|
1009
|
-
|
|
1010
|
-
const totalTools = toolSuccess + toolFailure + toolDenied;
|
|
1011
|
-
const toolRate = totalTools > 0 ? ((toolSuccess / totalTools) * 100).toFixed(1) : 100;
|
|
1012
|
-
document.getElementById('m-tool-rate').textContent = toolRate + '%';
|
|
1013
|
-
document.getElementById('m-tool-sub').textContent = \`\${toolSuccess} ok / \${toolFailure} err / \${toolDenied} denied\`;
|
|
1014
|
-
|
|
1015
|
-
document.getElementById('m-code-delta').innerHTML = \`<span style="color: #38bdf8;">+\${formatNumber(linesAdded)}</span> <span style="color: #64748b; font-size: 0.85em;">/</span> <span style="color: #f43f5e;">-\${formatNumber(linesRemoved)}</span>\`;
|
|
1016
|
-
document.getElementById('m-code-net').textContent = \`Net: \${linesAdded - linesRemoved >= 0 ? '+' : ''}\${formatNumber(linesAdded - linesRemoved)} lines\`;
|
|
1017
|
-
|
|
1018
|
-
if (rawData && rawData.budget) {
|
|
1019
|
-
renderBudget(rawData.budget);
|
|
1020
|
-
}
|
|
1021
|
-
|
|
1022
|
-
renderTimelineChart(filtered);
|
|
1023
|
-
renderProviderChart(providerTokens, totalTokens);
|
|
1024
|
-
renderToolChart(filtered);
|
|
1025
|
-
renderCodeChart(filtered);
|
|
1026
|
-
renderTable(filtered);
|
|
1027
|
-
}
|
|
1028
|
-
|
|
1029
|
-
function getProviderBrandIcon(providerName, size = 20) {
|
|
1030
|
-
const p = (providerName || '').toLowerCase().trim();
|
|
1031
|
-
if (p.includes('google') || p.includes('gemini')) {
|
|
1032
|
-
return \`<svg width="\${size}" height="\${size}" viewBox="0 0 24 24" fill="none" style="vertical-align: middle; flex-shrink: 0;" xmlns="http://www.w3.org/2000/svg">
|
|
1033
|
-
<defs>
|
|
1034
|
-
<linearGradient id="gg-gemini" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
1035
|
-
<stop offset="0%" stop-color="#4285F4"/>
|
|
1036
|
-
<stop offset="45%" stop-color="#9B72CB"/>
|
|
1037
|
-
<stop offset="100%" stop-color="#EA4335"/>
|
|
1038
|
-
</linearGradient>
|
|
1039
|
-
</defs>
|
|
1040
|
-
<path d="M12 1C12 7.075 7.075 12 1 12C7.075 12 12 16.925 12 23C12 16.925 16.925 12 23 12C16.925 12 12 7.075 12 1Z" fill="url(#gg-gemini)"/>
|
|
1041
|
-
</svg>\`;
|
|
1042
|
-
} else if (p.includes('nvidia')) {
|
|
1043
|
-
return \`<svg width="\${size}" height="\${size}" viewBox="0 0 24 24" fill="none" style="vertical-align: middle; flex-shrink: 0;" xmlns="http://www.w3.org/2000/svg">
|
|
1044
|
-
<path d="M9.85 7.15c.61-.17 1.43-.27 2.15-.27 3.32 0 6.01 2.37 6.01 5.3s-2.69 5.3-6.01 5.3c-.72 0-1.54-.1-2.15-.27V7.15zM2.5 12.18c0-3.32 2.3-6.17 5.75-6.9v13.8c-3.45-.73-5.75-3.58-5.75-6.9zm13.1 4.74c1.88-1.07 3.15-2.88 3.15-4.74 0-1.86-1.27-3.67-3.15-4.74l1.58-1.58C19.98 7.37 21.5 9.61 21.5 12.18s-1.52 4.81-4.32 6.32l-1.58-1.58z" fill="#76B900"/>
|
|
1045
|
-
</svg>\`;
|
|
1046
|
-
} else if (p.includes('deepseek')) {
|
|
1047
|
-
return \`<svg width="\${size}" height="\${size}" viewBox="0 0 24 24" fill="none" style="vertical-align: middle; flex-shrink: 0;" xmlns="http://www.w3.org/2000/svg">
|
|
1048
|
-
<path d="M12 2C6.48 2 2 6.48 2 12c0 3.82 2.15 7.14 5.3 8.84.18-.7.42-1.65.42-2.3 0-.82-.24-1.22-.52-1.74-.75-1.39-1.2-2.63-1.2-4.4 0-3.1 2.33-5.6 5.21-5.6 2.88 0 5.21 2.5 5.21 5.6 0 2.22-.73 4.14-2.11 5.42-.51.48-.96 1.05-.96 1.83 0 .7.26 1.58.42 2.24C17.65 20.25 22 16.63 22 12c0-5.52-4.48-10-10-10z" fill="#1D4ED8"/>
|
|
1049
|
-
<circle cx="10" cy="11.5" r="1.4" fill="#60A5FA"/>
|
|
1050
|
-
<circle cx="14" cy="11.5" r="1.4" fill="#60A5FA"/>
|
|
1051
|
-
</svg>\`;
|
|
1052
|
-
} else if (p.includes('mistral')) {
|
|
1053
|
-
return \`<svg width="\${size}" height="\${size}" viewBox="0 0 24 24" fill="none" style="vertical-align: middle; flex-shrink: 0;" xmlns="http://www.w3.org/2000/svg">
|
|
1054
|
-
<rect x="2" y="3" width="3.6" height="3.6" rx="0.5" fill="#FF7000"/>
|
|
1055
|
-
<rect x="18.4" y="3" width="3.6" height="3.6" rx="0.5" fill="#FF7000"/>
|
|
1056
|
-
<rect x="2" y="8.4" width="3.6" height="3.6" rx="0.5" fill="#FF7000"/>
|
|
1057
|
-
<rect x="7.4" y="8.4" width="3.6" height="3.6" rx="0.5" fill="#FF7000"/>
|
|
1058
|
-
<rect x="13" y="8.4" width="3.6" height="3.6" rx="0.5" fill="#FF7000"/>
|
|
1059
|
-
<rect x="18.4" y="8.4" width="3.6" height="3.6" rx="0.5" fill="#FF7000"/>
|
|
1060
|
-
<rect x="2" y="13.8" width="3.6" height="3.6" rx="0.5" fill="#FF7000"/>
|
|
1061
|
-
<rect x="7.4" y="13.8" width="3.6" height="3.6" rx="0.5" fill="#FF7000"/>
|
|
1062
|
-
<rect x="13" y="13.8" width="3.6" height="3.6" rx="0.5" fill="#FF7000"/>
|
|
1063
|
-
<rect x="18.4" y="13.8" width="3.6" height="3.6" rx="0.5" fill="#FF7000"/>
|
|
1064
|
-
<rect x="2" y="19.2" width="3.6" height="3.6" rx="0.5" fill="#FF7000"/>
|
|
1065
|
-
<rect x="18.4" y="19.2" width="3.6" height="3.6" rx="0.5" fill="#FF7000"/>
|
|
1066
|
-
</svg>\`;
|
|
1067
|
-
} else if (p.includes('openrouter')) {
|
|
1068
|
-
return \`<svg width="\${size}" height="\${size}" viewBox="0 0 24 24" fill="none" style="vertical-align: middle; flex-shrink: 0;" xmlns="http://www.w3.org/2000/svg">
|
|
1069
|
-
<rect x="3" y="3" width="7" height="7" rx="2" fill="#6366F1"/>
|
|
1070
|
-
<rect x="14" y="3" width="7" height="7" rx="2" fill="#38BDF8"/>
|
|
1071
|
-
<rect x="3" y="14" width="7" height="7" rx="2" fill="#38BDF8"/>
|
|
1072
|
-
<rect x="14" y="14" width="7" height="7" rx="2" fill="#6366F1"/>
|
|
1073
|
-
<path d="M10 6.5h4M6.5 10v4M17.5 10v4M10 17.5h4" stroke="rgba(255,255,255,0.85)" stroke-width="1.6" stroke-linecap="round"/>
|
|
1074
|
-
</svg>\`;
|
|
1075
|
-
} else if (p.includes('ollama')) {
|
|
1076
|
-
return \`<svg width="\${size}" height="\${size}" viewBox="0 0 24 24" fill="none" style="vertical-align: middle; flex-shrink: 0;" xmlns="http://www.w3.org/2000/svg">
|
|
1077
|
-
<path d="M8 3h3v4.5h2V4h3v8.5c0 2.2-1.8 4-4 4h-1v4.5H9V16.5H8c-2.2 0-4-1.8-4-4V7c0-2.2 1.8-4 4-4z" fill="#F59E0B"/>
|
|
1078
|
-
<circle cx="10" cy="5.8" r="1.1" fill="#1E293B"/>
|
|
1079
|
-
<circle cx="14" cy="5.8" r="1.1" fill="#1E293B"/>
|
|
1080
|
-
</svg>\`;
|
|
1081
|
-
} else if (p.includes('inferx')) {
|
|
1082
|
-
return \`<svg width="\${size}" height="\${size}" viewBox="0 0 24 24" fill="none" style="vertical-align: middle; flex-shrink: 0;" xmlns="http://www.w3.org/2000/svg">
|
|
1083
|
-
<defs>
|
|
1084
|
-
<linearGradient id="inf-grad" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
1085
|
-
<stop offset="0%" stop-color="#EC4899"/>
|
|
1086
|
-
<stop offset="100%" stop-color="#8B5CF6"/>
|
|
1087
|
-
</linearGradient>
|
|
1088
|
-
</defs>
|
|
1089
|
-
<path d="M4 4l6.5 7L4 20h3.5l4.5-5.2 4.5 5.2H20l-6.5-9L20 4h-3.5L12 9.2 7.5 4H4z" fill="url(#inf-grad)"/>
|
|
1090
|
-
</svg>\`;
|
|
1091
|
-
} else if (p.includes('sensenova') || p.includes('sense')) {
|
|
1092
|
-
return \`<svg width="\${size}" height="\${size}" viewBox="0 0 24 24" fill="none" style="vertical-align: middle; flex-shrink: 0;" xmlns="http://www.w3.org/2000/svg">
|
|
1093
|
-
<defs>
|
|
1094
|
-
<linearGradient id="sense-grad" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
1095
|
-
<stop offset="0%" stop-color="#06B6D4"/>
|
|
1096
|
-
<stop offset="100%" stop-color="#6366F1"/>
|
|
1097
|
-
</linearGradient>
|
|
1098
|
-
</defs>
|
|
1099
|
-
<circle cx="12" cy="12" r="8.5" stroke="url(#sense-grad)" stroke-width="2"/>
|
|
1100
|
-
<path d="M12 5v14M5 12h14M7 7l10 10M17 7L7 17" stroke="url(#sense-grad)" stroke-width="1.5" stroke-linecap="round"/>
|
|
1101
|
-
</svg>\`;
|
|
1102
|
-
} else if (p.includes('poolside') || p.includes('pool')) {
|
|
1103
|
-
return \`<svg width="\${size}" height="\${size}" viewBox="0 0 24 24" fill="none" style="vertical-align: middle; flex-shrink: 0;" xmlns="http://www.w3.org/2000/svg">
|
|
1104
|
-
<path d="M2 12C2 12 5 9 12 9C19 9 22 12 22 12C22 12 19 15 12 15C5 15 2 12 2 12Z" stroke="#38BDF8" stroke-width="2" stroke-linejoin="round"/>
|
|
1105
|
-
<circle cx="12" cy="12" r="2.5" fill="#38BDF8"/>
|
|
1106
|
-
<path d="M4 17C6 19 9 20 12 20C15 20 18 19 20 17" stroke="#38BDF8" stroke-width="1.5" stroke-linecap="round"/>
|
|
1107
|
-
</svg>\`;
|
|
1108
|
-
} else if (p.includes('9router') || p.includes('router')) {
|
|
1109
|
-
return \`<svg width="\${size}" height="\${size}" viewBox="0 0 24 24" fill="none" style="vertical-align: middle; flex-shrink: 0;" xmlns="http://www.w3.org/2000/svg">
|
|
1110
|
-
<circle cx="12" cy="12" r="9" stroke="#EC4899" stroke-width="2"/>
|
|
1111
|
-
<path d="M12 7v5l3 3" stroke="#EC4899" stroke-width="2" stroke-linecap="round"/>
|
|
1112
|
-
<circle cx="12" cy="12" r="2" fill="#EC4899"/>
|
|
1113
|
-
</svg>\`;
|
|
1114
|
-
} else if (p.includes('aihubmix') || p.includes('hubmix')) {
|
|
1115
|
-
return \`<svg width="${size}" height="${size}" viewBox="0 0 24 24" fill="none" style="vertical-align: middle; flex-shrink: 0;" xmlns="http://www.w3.org/2000/svg">
|
|
1116
|
-
<circle cx="12" cy="4.5" r="2.8" fill="#A855F7"/>
|
|
1117
|
-
<circle cx="5" cy="18" r="2.8" fill="#D946EF"/>
|
|
1118
|
-
<circle cx="19" cy="18" r="2.8" fill="#EC4899"/>
|
|
1119
|
-
<path d="M12 7.5v6.5M12 14L6.8 16.8M12 14l5.2 2.8" stroke="#A855F7" stroke-width="1.8" stroke-linecap="round"/>
|
|
1120
|
-
</svg>\`;
|
|
1121
|
-
} else if (p.includes('anthropic') || p.includes('claude')) {
|
|
1122
|
-
return \`<svg width="\${size}" height="\${size}" viewBox="0 0 24 24" fill="none" style="vertical-align: middle; flex-shrink: 0;" xmlns="http://www.w3.org/2000/svg">
|
|
1123
|
-
<path d="M14.5 3L8 21h3.5l1.3-3.8h4.4L18.5 21H22L15.5 3h-1zm.3 4.2l1.6 6.8h-3.2l1.6-6.8zM2 21h3.5L9.5 3H6L2 21z" fill="#D97757"/>
|
|
1124
|
-
</svg>\`;
|
|
1125
|
-
} else if (p.includes('openai')) {
|
|
1126
|
-
return \`<svg width="\${size}" height="\${size}" viewBox="0 0 24 24" fill="none" style="vertical-align: middle; flex-shrink: 0;" xmlns="http://www.w3.org/2000/svg">
|
|
1127
|
-
<path d="M20.5 10.3a5.5 5.5 0 0 0-.4-4.5 5.6 5.6 0 0 0-5.8-2.6A5.5 5.5 0 0 0 9.8 1.8a5.6 5.6 0 0 0-5.3 3.9 5.5 5.5 0 0 0-3.3 3.6 5.6 5.6 0 0 0 .9 6.2 5.5 5.5 0 0 0 .4 4.5 5.6 5.6 0 0 0 5.8 2.6 5.5 5.5 0 0 0 4.5 1.4 5.6 5.6 0 0 0 5.3-3.9 5.5 5.5 0 0 0 3.3-3.6 5.6 5.6 0 0 0-.9-6.2z" stroke="#10A37F" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
|
|
1128
|
-
</svg>\`;
|
|
1129
|
-
}
|
|
1130
|
-
return \`<span style="color: var(--accent-cyan); font-size: 1.1rem; vertical-align: middle;">\u2726</span>\`;
|
|
1131
|
-
}
|
|
1132
|
-
|
|
1133
|
-
function renderBudget(budgetData) {
|
|
1134
|
-
const grid = document.getElementById('budget-grid-cards');
|
|
1135
|
-
const tierPill = document.getElementById('budget-tier-pill');
|
|
1136
|
-
const resetBadge = document.getElementById('budget-reset-badge');
|
|
1137
|
-
const subtitleEl = document.getElementById('budget-subtitle');
|
|
1138
|
-
|
|
1139
|
-
if (!budgetData) {
|
|
1140
|
-
grid.innerHTML = '<div style="color: var(--text-dim); padding: 1rem;">No budget constraints configured.</div>';
|
|
1141
|
-
return;
|
|
1142
|
-
}
|
|
1143
|
-
|
|
1144
|
-
const isProviderMode = budgetData.providerBudgetsList && budgetData.providerBudgetsList.length > 0;
|
|
1145
|
-
|
|
1146
|
-
if (isProviderMode) {
|
|
1147
|
-
tierPill.style.display = 'inline-block';
|
|
1148
|
-
tierPill.className = 'badge-positive';
|
|
1149
|
-
tierPill.textContent = \`Provider Mode (\${budgetData.providerBudgetsList.length} Active)\`;
|
|
1150
|
-
subtitleEl.textContent = 'Enforcing provider-specific daily, monthly, and turn quotas';
|
|
1151
|
-
} else {
|
|
1152
|
-
tierPill.style.display = 'none';
|
|
1153
|
-
subtitleEl.textContent = 'Real-time spend against configured global daily and monthly token limits';
|
|
1154
|
-
}
|
|
1155
|
-
|
|
1156
|
-
resetBadge.textContent = 'Reset: ' + (budgetData.resetMode || 'None') + (budgetData.resetMode === 'Custom' ? \` (Day \${budgetData.resetDay})\` : '');
|
|
1157
|
-
|
|
1158
|
-
let cardsHtml = '';
|
|
1159
|
-
|
|
1160
|
-
if (isProviderMode) {
|
|
1161
|
-
budgetData.providerBudgetsList.forEach(prov => {
|
|
1162
|
-
const dailyTokens = prov.dailyTokens || 0;
|
|
1163
|
-
const dailyLimit = prov.dailyTokenLimit || 0;
|
|
1164
|
-
const isDailyUnlimited = prov.isDailyUnlimited;
|
|
1165
|
-
const dailyPct = (!isDailyUnlimited && dailyLimit > 0) ? Math.min(100, (dailyTokens / dailyLimit) * 100) : 0;
|
|
1166
|
-
const dailyColor = dailyPct >= 90 ? 'linear-gradient(90deg, #f43f5e, #e11d48)' : dailyPct >= 75 ? 'linear-gradient(90deg, #f59e0b, #d97706)' : 'linear-gradient(90deg, #38bdf8, #0284c7)';
|
|
1167
|
-
|
|
1168
|
-
const monthlyTokens = prov.monthlyTokens || 0;
|
|
1169
|
-
const monthlyLimit = prov.monthlyTokenLimit || 0;
|
|
1170
|
-
const isMonthlyUnlimited = prov.isMonthlyUnlimited;
|
|
1171
|
-
const monthlyPct = (!isMonthlyUnlimited && monthlyLimit > 0) ? Math.min(100, (monthlyTokens / monthlyLimit) * 100) : 0;
|
|
1172
|
-
const monthlyColor = monthlyPct >= 90 ? 'linear-gradient(90deg, #f43f5e, #e11d48)' : monthlyPct >= 75 ? 'linear-gradient(90deg, #f59e0b, #d97706)' : 'linear-gradient(90deg, #a855f7, #7c3aed)';
|
|
1173
|
-
|
|
1174
|
-
// Unified Provider Card grouping both Daily and Monthly
|
|
1175
|
-
cardsHtml += \`
|
|
1176
|
-
<div class="budget-card">
|
|
1177
|
-
<div class="budget-card-header">
|
|
1178
|
-
<div class="budget-group-title">
|
|
1179
|
-
\${getProviderBrandIcon(prov.provider, 22)}
|
|
1180
|
-
<span>\${prov.provider}</span>
|
|
1181
|
-
</div>
|
|
1182
|
-
</div>
|
|
1183
|
-
|
|
1184
|
-
<!-- Daily Budget Sub-Block -->
|
|
1185
|
-
<div class="budget-sub-block">
|
|
1186
|
-
<div class="budget-sub-header">
|
|
1187
|
-
<span class="budget-sub-title">\u26A1 Daily Token Budget</span>
|
|
1188
|
-
<span class="budget-tag" style="font-size: 0.68rem; padding: 1px 6px;">\${isDailyUnlimited ? 'Unlimited' : dailyPct.toFixed(1) + '%'}</span>
|
|
1189
|
-
</div>
|
|
1190
|
-
<div class="budget-values">
|
|
1191
|
-
<div class="budget-cur mono">\${formatNumber(dailyTokens)}</div>
|
|
1192
|
-
<div class="budget-max mono">/ \${isDailyUnlimited ? '\u221E Limit' : formatNumber(dailyLimit)}</div>
|
|
1193
|
-
</div>
|
|
1194
|
-
<div class="budget-progress-track">
|
|
1195
|
-
<div class="budget-progress-fill" style="width: \${isDailyUnlimited ? 100 : dailyPct}%; background: \${isDailyUnlimited ? 'rgba(56,189,248,0.3)' : dailyColor};"></div>
|
|
1196
|
-
</div>
|
|
1197
|
-
<div class="budget-footer">
|
|
1198
|
-
<span>\${isDailyUnlimited ? 'No daily cap' : \`\${formatNumber(Math.max(0, dailyLimit - dailyTokens))} remaining\`}</span>
|
|
1199
|
-
<span class="mono">\${isDailyUnlimited ? 'Active' : \`\${dailyPct.toFixed(0)}% used\`}</span>
|
|
1200
|
-
</div>
|
|
1201
|
-
</div>
|
|
1202
|
-
|
|
1203
|
-
<div class="budget-sub-divider"></div>
|
|
1204
|
-
|
|
1205
|
-
<!-- Monthly Budget Sub-Block -->
|
|
1206
|
-
<div class="budget-sub-block">
|
|
1207
|
-
<div class="budget-sub-header">
|
|
1208
|
-
<span class="budget-sub-title">\u{1F4C5} Monthly / Cycle Budget</span>
|
|
1209
|
-
<span class="budget-tag" style="font-size: 0.68rem; padding: 1px 6px;">\${isMonthlyUnlimited ? 'Unlimited' : monthlyPct.toFixed(1) + '%'}</span>
|
|
1210
|
-
</div>
|
|
1211
|
-
<div class="budget-values">
|
|
1212
|
-
<div class="budget-cur mono">\${formatNumber(monthlyTokens)}</div>
|
|
1213
|
-
<div class="budget-max mono">/ \${isMonthlyUnlimited ? '\u221E Limit' : formatNumber(monthlyLimit)}</div>
|
|
1214
|
-
</div>
|
|
1215
|
-
<div class="budget-progress-track">
|
|
1216
|
-
<div class="budget-progress-fill" style="width: \${isMonthlyUnlimited ? 100 : monthlyPct}%; background: \${isMonthlyUnlimited ? 'rgba(168,85,247,0.3)' : monthlyColor};"></div>
|
|
1217
|
-
</div>
|
|
1218
|
-
<div class="budget-footer">
|
|
1219
|
-
<span>\${isMonthlyUnlimited ? 'No monthly cap' : \`\${formatNumber(Math.max(0, monthlyLimit - monthlyTokens))} remaining\`}</span>
|
|
1220
|
-
<span class="mono">\${isMonthlyUnlimited ? 'Active' : \`\${monthlyPct.toFixed(0)}% used\`}</span>
|
|
1221
|
-
</div>
|
|
1222
|
-
</div>
|
|
1223
|
-
</div>
|
|
1224
|
-
\`;
|
|
1225
|
-
});
|
|
1226
|
-
} else {
|
|
1227
|
-
// Global Mode Cards
|
|
1228
|
-
const daily = budgetData.daily || {};
|
|
1229
|
-
const monthly = budgetData.monthly || {};
|
|
1230
|
-
|
|
1231
|
-
const dailyTokens = daily.tokensUsed || 0;
|
|
1232
|
-
const dailyLimit = daily.tokenLimit || 0;
|
|
1233
|
-
const isDailyUnlimited = daily.isUnlimitedTokens;
|
|
1234
|
-
const dailyPct = (!isDailyUnlimited && dailyLimit > 0) ? Math.min(100, (dailyTokens / dailyLimit) * 100) : 0;
|
|
1235
|
-
const dailyColor = dailyPct >= 90 ? 'linear-gradient(90deg, #f43f5e, #e11d48)' : dailyPct >= 75 ? 'linear-gradient(90deg, #f59e0b, #d97706)' : 'linear-gradient(90deg, #38bdf8, #0284c7)';
|
|
1236
|
-
|
|
1237
|
-
const monthlyTokens = monthly.tokensUsed || 0;
|
|
1238
|
-
const monthlyLimit = monthly.tokenLimit || 0;
|
|
1239
|
-
const isMonthlyUnlimited = monthly.isUnlimitedTokens;
|
|
1240
|
-
const monthlyPct = (!isMonthlyUnlimited && monthlyLimit > 0) ? Math.min(100, (monthlyTokens / monthlyLimit) * 100) : 0;
|
|
1241
|
-
const monthlyColor = monthlyPct >= 90 ? 'linear-gradient(90deg, #f43f5e, #e11d48)' : monthlyPct >= 75 ? 'linear-gradient(90deg, #f59e0b, #d97706)' : 'linear-gradient(90deg, #a855f7, #7c3aed)';
|
|
1242
|
-
|
|
1243
|
-
cardsHtml += \`
|
|
1244
|
-
<div class="budget-card">
|
|
1245
|
-
<div class="budget-card-header">
|
|
1246
|
-
<div class="budget-group-title">
|
|
1247
|
-
<span style="color: var(--accent-cyan);">\u26A1</span>
|
|
1248
|
-
<span>Global Budget Quotas</span>
|
|
1249
|
-
</div>
|
|
1250
|
-
<span class="budget-tag">Global Cap</span>
|
|
1251
|
-
</div>
|
|
1252
|
-
|
|
1253
|
-
<!-- Daily Budget Sub-Block -->
|
|
1254
|
-
<div class="budget-sub-block">
|
|
1255
|
-
<div class="budget-sub-header">
|
|
1256
|
-
<span class="budget-sub-title">\u26A1 Daily Token Budget</span>
|
|
1257
|
-
<span class="budget-tag" style="font-size: 0.68rem; padding: 1px 6px;">\${isDailyUnlimited ? 'Unlimited' : dailyPct.toFixed(1) + '%'}</span>
|
|
1258
|
-
</div>
|
|
1259
|
-
<div class="budget-values">
|
|
1260
|
-
<div class="budget-cur mono">\${formatNumber(dailyTokens)}</div>
|
|
1261
|
-
<div class="budget-max mono">/ \${isDailyUnlimited ? '\u221E Limit' : formatNumber(dailyLimit)}</div>
|
|
1262
|
-
</div>
|
|
1263
|
-
<div class="budget-progress-track">
|
|
1264
|
-
<div class="budget-progress-fill" style="width: \${isDailyUnlimited ? 100 : dailyPct}%; background: \${isDailyUnlimited ? 'rgba(56,189,248,0.3)' : dailyColor};"></div>
|
|
1265
|
-
</div>
|
|
1266
|
-
<div class="budget-footer">
|
|
1267
|
-
<span>\${isDailyUnlimited ? 'No daily cap enforced' : \`\${formatNumber(Math.max(0, dailyLimit - dailyTokens))} remaining\`}</span>
|
|
1268
|
-
<span class="mono">\${isDailyUnlimited ? 'Active' : \`\${dailyPct.toFixed(0)}% used\`}</span>
|
|
1269
|
-
</div>
|
|
1270
|
-
</div>
|
|
1271
|
-
|
|
1272
|
-
<div class="budget-sub-divider"></div>
|
|
1273
|
-
|
|
1274
|
-
<!-- Monthly Budget Sub-Block -->
|
|
1275
|
-
<div class="budget-sub-block">
|
|
1276
|
-
<div class="budget-sub-header">
|
|
1277
|
-
<span class="budget-sub-title">\u{1F4C5} Monthly / Cycle Budget</span>
|
|
1278
|
-
<span class="budget-tag" style="font-size: 0.68rem; padding: 1px 6px;">\${isMonthlyUnlimited ? 'Unlimited' : monthlyPct.toFixed(1) + '%'}</span>
|
|
1279
|
-
</div>
|
|
1280
|
-
<div class="budget-values">
|
|
1281
|
-
<div class="budget-cur mono">\${formatNumber(monthlyTokens)}</div>
|
|
1282
|
-
<div class="budget-max mono">/ \${isMonthlyUnlimited ? '\u221E Limit' : formatNumber(monthlyLimit)}</div>
|
|
1283
|
-
</div>
|
|
1284
|
-
<div class="budget-progress-track">
|
|
1285
|
-
<div class="budget-progress-fill" style="width: \${isMonthlyUnlimited ? 100 : monthlyPct}%; background: \${isMonthlyUnlimited ? 'rgba(168,85,247,0.3)' : monthlyColor};"></div>
|
|
1286
|
-
</div>
|
|
1287
|
-
<div class="budget-footer">
|
|
1288
|
-
<span>\${isMonthlyUnlimited ? 'No monthly cap enforced' : \`\${formatNumber(Math.max(0, monthlyLimit - monthlyTokens))} remaining\`}</span>
|
|
1289
|
-
<span class="mono">\${isMonthlyUnlimited ? 'Active' : \`\${monthlyPct.toFixed(0)}% used\`}</span>
|
|
1290
|
-
</div>
|
|
1291
|
-
</div>
|
|
1292
|
-
</div>
|
|
1293
|
-
\`;
|
|
1294
|
-
}
|
|
1295
|
-
|
|
1296
|
-
grid.innerHTML = cardsHtml;
|
|
1297
|
-
}
|
|
1298
|
-
|
|
1299
|
-
function renderTimelineChart(timeline) {
|
|
1300
|
-
const ctx = document.getElementById('timelineChart').getContext('2d');
|
|
1301
|
-
const labels = timeline.map(t => {
|
|
1302
|
-
const parts = t.date.split('-');
|
|
1303
|
-
return parts.length === 3 ? parts[1] + '/' + parts[2] : t.date;
|
|
1304
|
-
});
|
|
1305
|
-
|
|
1306
|
-
const uncachedPromptData = timeline.map(t => t.uncachedPromptTokens !== undefined ? t.uncachedPromptTokens : Math.max(0, (t.promptTokens || 0) - (t.cachedTokens || 0)));
|
|
1307
|
-
const cachedData = timeline.map(t => t.cachedTokens || 0);
|
|
1308
|
-
const candidateData = timeline.map(t => t.candidateTokens || 0);
|
|
1309
|
-
|
|
1310
|
-
if (timelineChart) timelineChart.destroy();
|
|
1311
|
-
|
|
1312
|
-
const isLine = chartType === 'line';
|
|
1313
|
-
|
|
1314
|
-
timelineChart = new Chart(ctx, {
|
|
1315
|
-
type: isLine ? 'line' : 'bar',
|
|
1316
|
-
data: {
|
|
1317
|
-
labels: labels,
|
|
1318
|
-
datasets: [
|
|
1319
|
-
{
|
|
1320
|
-
label: 'Input (Cached)',
|
|
1321
|
-
data: cachedData,
|
|
1322
|
-
backgroundColor: isLine ? 'rgba(4, 120, 87, 0.4)' : '#047857',
|
|
1323
|
-
borderColor: '#047857',
|
|
1324
|
-
borderWidth: isLine ? 2 : 0,
|
|
1325
|
-
fill: isLine,
|
|
1326
|
-
stack: 'tokens',
|
|
1327
|
-
tension: 0.3
|
|
1328
|
-
},
|
|
1329
|
-
{
|
|
1330
|
-
label: 'Input (Uncached)',
|
|
1331
|
-
data: uncachedPromptData,
|
|
1332
|
-
backgroundColor: isLine ? 'rgba(16, 185, 129, 0.65)' : '#10b981',
|
|
1333
|
-
borderColor: '#10b981',
|
|
1334
|
-
borderWidth: isLine ? 2 : 0,
|
|
1335
|
-
fill: isLine,
|
|
1336
|
-
stack: 'tokens',
|
|
1337
|
-
tension: 0.3
|
|
1338
|
-
},
|
|
1339
|
-
{
|
|
1340
|
-
label: 'Output (Candidate)',
|
|
1341
|
-
data: candidateData,
|
|
1342
|
-
backgroundColor: isLine ? 'rgba(168, 85, 247, 0.65)' : '#a855f7',
|
|
1343
|
-
borderColor: '#a855f7',
|
|
1344
|
-
borderWidth: isLine ? 2 : 0,
|
|
1345
|
-
fill: isLine,
|
|
1346
|
-
stack: 'tokens',
|
|
1347
|
-
tension: 0.3
|
|
1348
|
-
}
|
|
1349
|
-
]
|
|
1350
|
-
},
|
|
1351
|
-
options: {
|
|
1352
|
-
responsive: true,
|
|
1353
|
-
maintainAspectRatio: false,
|
|
1354
|
-
interaction: { mode: 'index', intersect: false },
|
|
1355
|
-
plugins: {
|
|
1356
|
-
legend: {
|
|
1357
|
-
position: 'top',
|
|
1358
|
-
labels: { color: '#94a3b8', boxWidth: 12, font: { family: 'Plus Jakarta Sans', size: 11, weight: '600' } }
|
|
1359
|
-
},
|
|
1360
|
-
tooltip: {
|
|
1361
|
-
backgroundColor: 'rgba(15, 23, 42, 0.95)',
|
|
1362
|
-
titleColor: '#f8fafc',
|
|
1363
|
-
bodyColor: '#e2e8f0',
|
|
1364
|
-
borderColor: 'rgba(255, 255, 255, 0.12)',
|
|
1365
|
-
borderWidth: 1,
|
|
1366
|
-
padding: 10,
|
|
1367
|
-
boxPadding: 4,
|
|
1368
|
-
callbacks: {
|
|
1369
|
-
labelColor: function(context) {
|
|
1370
|
-
const c = context.dataset.borderColor || context.dataset.backgroundColor;
|
|
1371
|
-
return {
|
|
1372
|
-
borderColor: c,
|
|
1373
|
-
backgroundColor: c
|
|
1374
|
-
};
|
|
1375
|
-
},
|
|
1376
|
-
label: function(context) {
|
|
1377
|
-
return context.dataset.label + ': ' + context.parsed.y.toLocaleString() + ' tokens';
|
|
1378
|
-
}
|
|
1379
|
-
}
|
|
1380
|
-
}
|
|
1381
|
-
},
|
|
1382
|
-
scales: {
|
|
1383
|
-
x: {
|
|
1384
|
-
stacked: true,
|
|
1385
|
-
grid: { color: 'rgba(255, 255, 255, 0.05)' },
|
|
1386
|
-
ticks: { color: '#64748b' }
|
|
1387
|
-
},
|
|
1388
|
-
y: {
|
|
1389
|
-
stacked: true,
|
|
1390
|
-
grid: { color: 'rgba(255, 255, 255, 0.05)' },
|
|
1391
|
-
ticks: {
|
|
1392
|
-
color: '#64748b',
|
|
1393
|
-
callback: val => formatNumber(val)
|
|
1394
|
-
}
|
|
1395
|
-
}
|
|
1396
|
-
}
|
|
1397
|
-
}
|
|
1398
|
-
});
|
|
1399
|
-
}
|
|
1400
|
-
|
|
1401
|
-
function renderProviderChart(providerTokens, totalTokens) {
|
|
1402
|
-
const ctx = document.getElementById('providerPieChart').getContext('2d');
|
|
1403
|
-
const listEl = document.getElementById('provider-breakdown-list');
|
|
1404
|
-
|
|
1405
|
-
const providers = Object.keys(providerTokens).sort((a, b) => (providerTokens[b] || 0) - (providerTokens[a] || 0));
|
|
1406
|
-
const dataVals = providers.map(p => providerTokens[p]);
|
|
1407
|
-
|
|
1408
|
-
if (providerPieChart) providerPieChart.destroy();
|
|
1409
|
-
|
|
1410
|
-
const colors = ['#38bdf8', '#a855f7', '#10b981', '#f59e0b', '#f43f5e', '#3b82f6', '#06b6d4'];
|
|
1411
|
-
|
|
1412
|
-
if (providers.length === 0) {
|
|
1413
|
-
listEl.innerHTML = '<div style="color: var(--text-dim); font-size: 0.8rem; text-align: center; padding: 1rem;">No multi-provider usage recorded yet.</div>';
|
|
1414
|
-
return;
|
|
1415
|
-
}
|
|
1416
|
-
|
|
1417
|
-
providerPieChart = new Chart(ctx, {
|
|
1418
|
-
type: 'doughnut',
|
|
1419
|
-
data: {
|
|
1420
|
-
labels: providers,
|
|
1421
|
-
datasets: [{
|
|
1422
|
-
data: dataVals,
|
|
1423
|
-
backgroundColor: colors.slice(0, providers.length),
|
|
1424
|
-
borderWidth: 0,
|
|
1425
|
-
hoverOffset: 4
|
|
1426
|
-
}]
|
|
1427
|
-
},
|
|
1428
|
-
options: {
|
|
1429
|
-
responsive: true,
|
|
1430
|
-
maintainAspectRatio: false,
|
|
1431
|
-
cutout: '70%',
|
|
1432
|
-
plugins: {
|
|
1433
|
-
legend: { display: false }
|
|
1434
|
-
}
|
|
1435
|
-
}
|
|
1436
|
-
});
|
|
1437
|
-
|
|
1438
|
-
listEl.innerHTML = providers.map((p, idx) => {
|
|
1439
|
-
const count = providerTokens[p];
|
|
1440
|
-
const pct = totalTokens > 0 ? ((count / totalTokens) * 100).toFixed(1) : 0;
|
|
1441
|
-
const col = colors[idx % colors.length];
|
|
1442
|
-
return \`
|
|
1443
|
-
<div class="breakdown-item">
|
|
1444
|
-
<div class="breakdown-header">
|
|
1445
|
-
<span style="display: flex; align-items: center; gap: 8px;">
|
|
1446
|
-
\${getProviderBrandIcon(p, 16)}
|
|
1447
|
-
<strong style="color: var(--text-main);">\${p}</strong>
|
|
1448
|
-
</span>
|
|
1449
|
-
<span class="mono" style="color: var(--text-muted);">\${formatNumber(count)} tokens (\${pct}%)</span>
|
|
1450
|
-
</div>
|
|
1451
|
-
<div class="progress-bar-bg">
|
|
1452
|
-
<div class="progress-bar-fill" style="width: \${pct}%; background: \${col};"></div>
|
|
1453
|
-
</div>
|
|
1454
|
-
</div>
|
|
1455
|
-
\`;
|
|
1456
|
-
}).join('');
|
|
1457
|
-
}
|
|
1458
|
-
|
|
1459
|
-
function renderToolChart(timeline) {
|
|
1460
|
-
const ctx = document.getElementById('toolChart').getContext('2d');
|
|
1461
|
-
const labels = timeline.map(t => t.date.split('-').slice(1).join('/'));
|
|
1462
|
-
|
|
1463
|
-
if (toolChart) toolChart.destroy();
|
|
1464
|
-
|
|
1465
|
-
toolChart = new Chart(ctx, {
|
|
1466
|
-
type: 'bar',
|
|
1467
|
-
data: {
|
|
1468
|
-
labels: labels,
|
|
1469
|
-
datasets: [
|
|
1470
|
-
{
|
|
1471
|
-
label: 'Success',
|
|
1472
|
-
data: timeline.map(t => t.toolSuccess || 0),
|
|
1473
|
-
backgroundColor: 'rgba(16, 185, 129, 0.75)',
|
|
1474
|
-
borderRadius: 4
|
|
1475
|
-
},
|
|
1476
|
-
{
|
|
1477
|
-
label: 'Failed',
|
|
1478
|
-
data: timeline.map(t => t.toolFailure || 0),
|
|
1479
|
-
backgroundColor: 'rgba(244, 63, 94, 0.75)',
|
|
1480
|
-
borderRadius: 4
|
|
1481
|
-
},
|
|
1482
|
-
{
|
|
1483
|
-
label: 'Denied',
|
|
1484
|
-
data: timeline.map(t => t.toolDenied || 0),
|
|
1485
|
-
backgroundColor: 'rgba(245, 158, 11, 0.75)',
|
|
1486
|
-
borderRadius: 4
|
|
1487
|
-
}
|
|
1488
|
-
]
|
|
1489
|
-
},
|
|
1490
|
-
options: {
|
|
1491
|
-
responsive: true,
|
|
1492
|
-
maintainAspectRatio: false,
|
|
1493
|
-
scales: {
|
|
1494
|
-
x: { stacked: true, grid: { display: false } },
|
|
1495
|
-
y: { stacked: true, grid: { color: 'rgba(255, 255, 255, 0.05)' } }
|
|
1496
|
-
},
|
|
1497
|
-
plugins: {
|
|
1498
|
-
legend: { position: 'bottom' }
|
|
1499
|
-
}
|
|
1500
|
-
}
|
|
1501
|
-
});
|
|
1502
|
-
}
|
|
1503
|
-
|
|
1504
|
-
function renderCodeChart(timeline) {
|
|
1505
|
-
const ctx = document.getElementById('codeChart').getContext('2d');
|
|
1506
|
-
const labels = timeline.map(t => t.date.split('-').slice(1).join('/'));
|
|
1507
|
-
|
|
1508
|
-
if (codeChart) codeChart.destroy();
|
|
1509
|
-
|
|
1510
|
-
codeChart = new Chart(ctx, {
|
|
1511
|
-
type: 'bar',
|
|
1512
|
-
data: {
|
|
1513
|
-
labels: labels,
|
|
1514
|
-
datasets: [
|
|
1515
|
-
{
|
|
1516
|
-
label: 'Lines Added (+)',
|
|
1517
|
-
data: timeline.map(t => t.linesAdded || 0),
|
|
1518
|
-
backgroundColor: 'rgba(56, 189, 248, 0.85)',
|
|
1519
|
-
borderRadius: { topLeft: 4, topRight: 4, bottomLeft: 0, bottomRight: 0 },
|
|
1520
|
-
stack: 'codeStack'
|
|
1521
|
-
},
|
|
1522
|
-
{
|
|
1523
|
-
label: 'Lines Removed (-)',
|
|
1524
|
-
data: timeline.map(t => -(t.linesRemoved || 0)),
|
|
1525
|
-
backgroundColor: 'rgba(244, 63, 94, 0.85)',
|
|
1526
|
-
borderRadius: { topLeft: 0, topRight: 0, bottomLeft: 4, bottomRight: 4 },
|
|
1527
|
-
stack: 'codeStack'
|
|
1528
|
-
}
|
|
1529
|
-
]
|
|
1530
|
-
},
|
|
1531
|
-
options: {
|
|
1532
|
-
responsive: true,
|
|
1533
|
-
maintainAspectRatio: false,
|
|
1534
|
-
scales: {
|
|
1535
|
-
x: {
|
|
1536
|
-
stacked: true,
|
|
1537
|
-
grid: { display: false },
|
|
1538
|
-
ticks: { color: '#64748b' }
|
|
1539
|
-
},
|
|
1540
|
-
y: {
|
|
1541
|
-
stacked: true,
|
|
1542
|
-
grid: {
|
|
1543
|
-
color: function(context) {
|
|
1544
|
-
if (context.tick && context.tick.value === 0) {
|
|
1545
|
-
return 'rgba(255, 255, 255, 0.25)';
|
|
1546
|
-
}
|
|
1547
|
-
return 'rgba(255, 255, 255, 0.05)';
|
|
1548
|
-
}
|
|
1549
|
-
},
|
|
1550
|
-
ticks: {
|
|
1551
|
-
color: '#64748b',
|
|
1552
|
-
callback: function(val) {
|
|
1553
|
-
const abs = Math.abs(val);
|
|
1554
|
-
if (abs === 0) return '0';
|
|
1555
|
-
return (val > 0 ? '+' : '-') + formatNumber(abs);
|
|
1556
|
-
}
|
|
1557
|
-
}
|
|
1558
|
-
}
|
|
1559
|
-
},
|
|
1560
|
-
plugins: {
|
|
1561
|
-
legend: {
|
|
1562
|
-
position: 'bottom',
|
|
1563
|
-
labels: { color: '#94a3b8', boxWidth: 12, font: { family: 'Plus Jakarta Sans', size: 11, weight: '600' } }
|
|
1564
|
-
},
|
|
1565
|
-
tooltip: {
|
|
1566
|
-
callbacks: {
|
|
1567
|
-
label: function(context) {
|
|
1568
|
-
const val = context.raw;
|
|
1569
|
-
const abs = Math.abs(val);
|
|
1570
|
-
return (context.dataset.label || '') + ': ' + (val >= 0 ? '+' : '-') + abs.toLocaleString() + ' lines';
|
|
1571
|
-
}
|
|
1572
|
-
}
|
|
1573
|
-
}
|
|
1574
|
-
}
|
|
1575
|
-
}
|
|
1576
|
-
});
|
|
1577
|
-
}
|
|
1578
|
-
|
|
1579
|
-
function getSortIndicator(field, currentField, currentOrder) {
|
|
1580
|
-
if (field !== currentField) {
|
|
1581
|
-
return '<span class="sort-icon" style="opacity: 0.35;">\u21C5</span>';
|
|
1582
|
-
}
|
|
1583
|
-
return currentOrder === 'asc'
|
|
1584
|
-
? '<span class="sort-icon" style="color: var(--accent-cyan); opacity: 1; font-weight: bold;">\u2191</span>'
|
|
1585
|
-
: '<span class="sort-icon" style="color: var(--accent-cyan); opacity: 1; font-weight: bold;">\u2193</span>';
|
|
1586
|
-
}
|
|
1587
|
-
|
|
1588
|
-
window.handleSort = function(field) {
|
|
1589
|
-
if (tableMode === 'daily') {
|
|
1590
|
-
if (dailySortField === field) {
|
|
1591
|
-
dailySortOrder = dailySortOrder === 'asc' ? 'desc' : 'asc';
|
|
1592
|
-
} else {
|
|
1593
|
-
dailySortField = field;
|
|
1594
|
-
dailySortOrder = (field === 'date') ? 'desc' : 'desc';
|
|
1595
|
-
}
|
|
1596
|
-
} else {
|
|
1597
|
-
if (modelSortField === field) {
|
|
1598
|
-
modelSortOrder = modelSortOrder === 'asc' ? 'desc' : 'asc';
|
|
1599
|
-
} else {
|
|
1600
|
-
modelSortField = field;
|
|
1601
|
-
modelSortOrder = (field === 'model' || field === 'provider') ? 'asc' : 'desc';
|
|
1602
|
-
}
|
|
1603
|
-
}
|
|
1604
|
-
if (rawData && rawData.timeline) {
|
|
1605
|
-
renderTable(filterTimelineByRange(rawData.timeline, activeRange));
|
|
1606
|
-
}
|
|
1607
|
-
};
|
|
1608
|
-
|
|
1609
|
-
function renderTable(timeline) {
|
|
1610
|
-
const thead = document.getElementById('usage-table-head');
|
|
1611
|
-
const tbody = document.getElementById('usage-table-body');
|
|
1612
|
-
const titleEl = document.getElementById('table-view-title');
|
|
1613
|
-
const subtitleEl = document.getElementById('table-view-subtitle');
|
|
1614
|
-
const searchInput = document.getElementById('table-search');
|
|
1615
|
-
const filterText = (searchInput.value || '').toLowerCase();
|
|
1616
|
-
|
|
1617
|
-
if (tableMode === 'daily') {
|
|
1618
|
-
titleEl.textContent = 'Daily Detailed Token Records';
|
|
1619
|
-
subtitleEl.textContent = 'Itemized log of daily token counts, cache ratios, and tool operations (click headers to sort)';
|
|
1620
|
-
searchInput.placeholder = 'Filter by date...';
|
|
1621
|
-
|
|
1622
|
-
thead.innerHTML = \`
|
|
1623
|
-
<tr>
|
|
1624
|
-
<th class="sortable" onclick="handleSort('date')">Date \${getSortIndicator('date', dailySortField, dailySortOrder)}</th>
|
|
1625
|
-
<th class="sortable" onclick="handleSort('tokens')">Total Tokens \${getSortIndicator('tokens', dailySortField, dailySortOrder)}</th>
|
|
1626
|
-
<th class="sortable" onclick="handleSort('promptTokens')">Prompt (In) \${getSortIndicator('promptTokens', dailySortField, dailySortOrder)}</th>
|
|
1627
|
-
<th class="sortable" onclick="handleSort('candidateTokens')">Candidate (Out) \${getSortIndicator('candidateTokens', dailySortField, dailySortOrder)}</th>
|
|
1628
|
-
<th class="sortable" onclick="handleSort('cachedTokens')">Cached Tokens \${getSortIndicator('cachedTokens', dailySortField, dailySortOrder)}</th>
|
|
1629
|
-
<th class="sortable" onclick="handleSort('cachePct')">Cache % \${getSortIndicator('cachePct', dailySortField, dailySortOrder)}</th>
|
|
1630
|
-
<th class="sortable" onclick="handleSort('requests')">Requests \${getSortIndicator('requests', dailySortField, dailySortOrder)}</th>
|
|
1631
|
-
<th class="sortable" onclick="handleSort('toolSuccessRate')">Tool Success \${getSortIndicator('toolSuccessRate', dailySortField, dailySortOrder)}</th>
|
|
1632
|
-
<th class="sortable col-code" onclick="handleSort('linesAdded')">Code Lines \${getSortIndicator('linesAdded', dailySortField, dailySortOrder)}</th>
|
|
1633
|
-
</tr>
|
|
1634
|
-
\`;
|
|
1635
|
-
|
|
1636
|
-
let rows = [...timeline].filter(item => {
|
|
1637
|
-
return !filterText || item.date.toLowerCase().includes(filterText);
|
|
1638
|
-
});
|
|
1639
|
-
|
|
1640
|
-
rows.sort((a, b) => {
|
|
1641
|
-
let valA, valB;
|
|
1642
|
-
if (dailySortField === 'date') {
|
|
1643
|
-
valA = new Date(a.date).getTime();
|
|
1644
|
-
valB = new Date(b.date).getTime();
|
|
1645
|
-
} else if (dailySortField === 'requests') {
|
|
1646
|
-
valA = a.totalRequests || 0;
|
|
1647
|
-
valB = b.totalRequests || 0;
|
|
1648
|
-
} else if (dailySortField === 'cachePct') {
|
|
1649
|
-
valA = (a.promptTokens || 0) > 0 ? ((a.cachedTokens || 0) / (a.promptTokens || 1)) : 0;
|
|
1650
|
-
valB = (b.promptTokens || 0) > 0 ? ((b.cachedTokens || 0) / (b.promptTokens || 1)) : 0;
|
|
1651
|
-
} else if (dailySortField === 'linesAdded') {
|
|
1652
|
-
valA = (a.linesAdded || 0) - (a.linesRemoved || 0);
|
|
1653
|
-
valB = (b.linesAdded || 0) - (b.linesRemoved || 0);
|
|
1654
|
-
} else {
|
|
1655
|
-
valA = a[dailySortField] || 0;
|
|
1656
|
-
valB = b[dailySortField] || 0;
|
|
1657
|
-
}
|
|
1658
|
-
return dailySortOrder === 'asc' ? (valA > valB ? 1 : -1) : (valA < valB ? 1 : -1);
|
|
1659
|
-
});
|
|
1660
|
-
|
|
1661
|
-
if (rows.length === 0) {
|
|
1662
|
-
tbody.innerHTML = '<tr><td colspan="9" style="text-align: center; color: var(--text-dim); padding: 2rem;">No matching daily token records found.</td></tr>';
|
|
1663
|
-
return;
|
|
1664
|
-
}
|
|
1665
|
-
|
|
1666
|
-
tbody.innerHTML = rows.map(item => {
|
|
1667
|
-
const total = item.tokens || 0;
|
|
1668
|
-
const cached = item.cachedTokens || 0;
|
|
1669
|
-
const prompt = item.promptTokens || 0;
|
|
1670
|
-
const cand = item.candidateTokens || 0;
|
|
1671
|
-
const cachePct = prompt > 0 ? ((cached / prompt) * 100).toFixed(1) : '0.0';
|
|
1672
|
-
|
|
1673
|
-
return \`
|
|
1674
|
-
<tr>
|
|
1675
|
-
<td class="mono" style="font-weight: 600; color: var(--accent-cyan);">\${item.date}</td>
|
|
1676
|
-
<td class="mono" style="font-weight: 700;">\${formatNumber(total)}</td>
|
|
1677
|
-
<td class="mono" style="color: var(--accent-emerald);">\${formatNumber(prompt)}</td>
|
|
1678
|
-
<td class="mono" style="color: var(--accent-violet);">\${formatNumber(cand)}</td>
|
|
1679
|
-
<td class="mono" style="color: #059669;">\${formatNumber(cached)}</td>
|
|
1680
|
-
<td>
|
|
1681
|
-
<span class="badge-positive" style="font-size: 0.75rem;">\${cachePct}%</span>
|
|
1682
|
-
</td>
|
|
1683
|
-
<td>\${item.totalRequests || 0} req</td>
|
|
1684
|
-
<td>
|
|
1685
|
-
<span class="badge-info" style="font-size: 0.75rem;">\${item.toolSuccessRate}%</span>
|
|
1686
|
-
</td>
|
|
1687
|
-
<td class="mono col-code">
|
|
1688
|
-
<span style="color: #38bdf8;">+\${formatNumber(item.linesAdded)}</span> /
|
|
1689
|
-
<span style="color: #f43f5e;">-\${formatNumber(item.linesRemoved)}</span>
|
|
1690
|
-
</td>
|
|
1691
|
-
</tr>
|
|
1692
|
-
\`;
|
|
1693
|
-
}).join('');
|
|
1694
|
-
} else {
|
|
1695
|
-
titleEl.textContent = 'Model Token Statistics';
|
|
1696
|
-
subtitleEl.textContent = 'Aggregated token distribution, cache performance and share per model (click headers to sort)';
|
|
1697
|
-
searchInput.placeholder = 'Filter by model or provider...';
|
|
1698
|
-
|
|
1699
|
-
thead.innerHTML = \`
|
|
1700
|
-
<tr>
|
|
1701
|
-
<th class="sortable" onclick="handleSort('model')">Model Name \${getSortIndicator('model', modelSortField, modelSortOrder)}</th>
|
|
1702
|
-
<th class="sortable" onclick="handleSort('provider')">Provider \${getSortIndicator('provider', modelSortField, modelSortOrder)}</th>
|
|
1703
|
-
<th class="sortable" onclick="handleSort('tokens')">Total Tokens \${getSortIndicator('tokens', modelSortField, modelSortOrder)}</th>
|
|
1704
|
-
<th class="sortable" onclick="handleSort('promptTokens')">Prompt (In) \${getSortIndicator('promptTokens', modelSortField, modelSortOrder)}</th>
|
|
1705
|
-
<th class="sortable" onclick="handleSort('candidateTokens')">Candidate (Out) \${getSortIndicator('candidateTokens', modelSortField, modelSortOrder)}</th>
|
|
1706
|
-
<th class="sortable" onclick="handleSort('cachedTokens')">Cached Tokens \${getSortIndicator('cachedTokens', modelSortField, modelSortOrder)}</th>
|
|
1707
|
-
<th class="sortable" onclick="handleSort('cachePct')">Cache % \${getSortIndicator('cachePct', modelSortField, modelSortOrder)}</th>
|
|
1708
|
-
<th class="sortable" onclick="handleSort('sharePct')">Token Share \${getSortIndicator('sharePct', modelSortField, modelSortOrder)}</th>
|
|
1709
|
-
</tr>
|
|
1710
|
-
\`;
|
|
1711
|
-
|
|
1712
|
-
const modelMap = {};
|
|
1713
|
-
let aggregateTokens = 0;
|
|
1714
|
-
|
|
1715
|
-
timeline.forEach(day => {
|
|
1716
|
-
const models = day.models || {};
|
|
1717
|
-
for (const prov in models) {
|
|
1718
|
-
for (const m in models[prov]) {
|
|
1719
|
-
const entry = models[prov][m] || {};
|
|
1720
|
-
const key = prov + '___' + m;
|
|
1721
|
-
if (!modelMap[key]) {
|
|
1722
|
-
modelMap[key] = {
|
|
1723
|
-
model: m,
|
|
1724
|
-
provider: prov,
|
|
1725
|
-
tokens: 0,
|
|
1726
|
-
promptTokens: 0,
|
|
1727
|
-
candidateTokens: 0,
|
|
1728
|
-
cachedTokens: 0
|
|
1729
|
-
};
|
|
1730
|
-
}
|
|
1731
|
-
const tTok = entry.tokens || 0;
|
|
1732
|
-
const tCand = entry.candidateTokens || 0;
|
|
1733
|
-
const tPrompt = (entry.promptTokens !== undefined && entry.promptTokens !== null)
|
|
1734
|
-
? entry.promptTokens
|
|
1735
|
-
: Math.max(0, tTok - tCand);
|
|
1736
|
-
const tCache = entry.cachedTokens || 0;
|
|
1737
|
-
|
|
1738
|
-
modelMap[key].tokens += tTok;
|
|
1739
|
-
modelMap[key].promptTokens += tPrompt;
|
|
1740
|
-
modelMap[key].candidateTokens += tCand;
|
|
1741
|
-
modelMap[key].cachedTokens += tCache;
|
|
1742
|
-
aggregateTokens += tTok;
|
|
1743
|
-
}
|
|
1744
|
-
}
|
|
1745
|
-
});
|
|
1746
|
-
|
|
1747
|
-
let modelList = Object.values(modelMap).map(item => {
|
|
1748
|
-
const cachePct = item.promptTokens > 0
|
|
1749
|
-
? ((item.cachedTokens / item.promptTokens) * 100).toFixed(1)
|
|
1750
|
-
: (item.tokens > 0 ? ((item.cachedTokens / item.tokens) * 100).toFixed(1) : '0.0');
|
|
1751
|
-
const sharePct = aggregateTokens > 0 ? ((item.tokens / aggregateTokens) * 100).toFixed(1) : '0.0';
|
|
1752
|
-
return {
|
|
1753
|
-
...item,
|
|
1754
|
-
cachePct: parseFloat(cachePct),
|
|
1755
|
-
sharePct: parseFloat(sharePct)
|
|
1756
|
-
};
|
|
1757
|
-
});
|
|
1758
|
-
|
|
1759
|
-
modelList.sort((a, b) => {
|
|
1760
|
-
let valA = a[modelSortField];
|
|
1761
|
-
let valB = b[modelSortField];
|
|
1762
|
-
if (modelSortField === 'model' || modelSortField === 'provider') {
|
|
1763
|
-
valA = (valA || '').toLowerCase();
|
|
1764
|
-
valB = (valB || '').toLowerCase();
|
|
1765
|
-
return modelSortOrder === 'asc' ? valA.localeCompare(valB) : valB.localeCompare(valA);
|
|
1766
|
-
} else {
|
|
1767
|
-
valA = valA || 0;
|
|
1768
|
-
valB = valB || 0;
|
|
1769
|
-
return modelSortOrder === 'asc' ? (valA > valB ? 1 : -1) : (valA < valB ? 1 : -1);
|
|
1770
|
-
}
|
|
1771
|
-
});
|
|
1772
|
-
|
|
1773
|
-
if (filterText) {
|
|
1774
|
-
modelList = modelList.filter(m => {
|
|
1775
|
-
return m.model.toLowerCase().includes(filterText) || m.provider.toLowerCase().includes(filterText);
|
|
1776
|
-
});
|
|
1777
|
-
}
|
|
1778
|
-
|
|
1779
|
-
if (modelList.length === 0) {
|
|
1780
|
-
tbody.innerHTML = '<tr><td colspan="8" style="text-align: center; color: var(--text-dim); padding: 2rem;">No matching models found.</td></tr>';
|
|
1781
|
-
return;
|
|
1782
|
-
}
|
|
1783
|
-
|
|
1784
|
-
tbody.innerHTML = modelList.map(m => {
|
|
1785
|
-
return \`
|
|
1786
|
-
<tr>
|
|
1787
|
-
<td class="mono" style="font-weight: 600; color: var(--text-main);">
|
|
1788
|
-
<span style="display: inline-flex; align-items: center; gap: 8px;">
|
|
1789
|
-
\${getProviderBrandIcon(m.provider, 16)}
|
|
1790
|
-
<span>\${m.model}</span>
|
|
1791
|
-
</span>
|
|
1792
|
-
</td>
|
|
1793
|
-
<td>
|
|
1794
|
-
<span class="badge-info" style="font-size: 0.75rem; display: inline-flex; align-items: center; gap: 5px;">
|
|
1795
|
-
\${m.provider}
|
|
1796
|
-
</span>
|
|
1797
|
-
</td>
|
|
1798
|
-
<td class="mono" style="font-weight: 700; color: var(--text-main);">\${formatNumber(m.tokens)}</td>
|
|
1799
|
-
<td class="mono" style="color: var(--accent-emerald);">\${formatNumber(m.promptTokens)}</td>
|
|
1800
|
-
<td class="mono" style="color: var(--accent-violet);">\${formatNumber(m.candidateTokens)}</td>
|
|
1801
|
-
<td class="mono" style="color: #059669;">\${formatNumber(m.cachedTokens)}</td>
|
|
1802
|
-
<td>
|
|
1803
|
-
<span class="badge-positive" style="font-size: 0.75rem;">\${m.cachePct}%</span>
|
|
1804
|
-
</td>
|
|
1805
|
-
<td>
|
|
1806
|
-
<div style="display: flex; align-items: center; gap: 8px;">
|
|
1807
|
-
<div style="flex: 1; min-width: 60px; height: 6px; background: rgba(255,255,255,0.06); border-radius: 999px; overflow: hidden;">
|
|
1808
|
-
<div style="width: \${m.sharePct}%; height: 100%; background: linear-gradient(90deg, #38bdf8, #a855f7); border-radius: 999px;"></div>
|
|
1809
|
-
</div>
|
|
1810
|
-
<span class="mono" style="font-size: 0.75rem; color: var(--text-muted); min-width: 38px;">\${m.sharePct}%</span>
|
|
1811
|
-
</div>
|
|
1812
|
-
</td>
|
|
1813
|
-
</tr>
|
|
1814
|
-
\`;
|
|
1815
|
-
}).join('');
|
|
1816
|
-
}
|
|
1817
|
-
}
|
|
1818
|
-
|
|
1819
|
-
function exportJson() {
|
|
1820
|
-
if (!rawData) return;
|
|
1821
|
-
const blob = new Blob([JSON.stringify(rawData, null, 2)], { type: 'application/json' });
|
|
1822
|
-
const url = URL.createObjectURL(blob);
|
|
1823
|
-
const a = document.createElement('a');
|
|
1824
|
-
a.href = url;
|
|
1825
|
-
a.download = \`fluxflow-token-usage-\${new Date().toISOString().split('T')[0]}.json\`;
|
|
1826
|
-
a.click();
|
|
1827
|
-
URL.revokeObjectURL(url);
|
|
1828
|
-
}
|
|
1829
|
-
|
|
1830
|
-
function exportCsv() {
|
|
1831
|
-
if (!rawData || !rawData.timeline) return;
|
|
1832
|
-
const headers = ['Date', 'TotalTokens', 'PromptTokens', 'CandidateTokens', 'CachedTokens', 'CachePercent', 'AgentRequests', 'BackgroundRequests', 'SearchRequests', 'ToolSuccess', 'ToolFailure', 'ToolDenied', 'LinesAdded', 'LinesRemoved'];
|
|
1833
|
-
const csvRows = [headers.join(',')];
|
|
1834
|
-
|
|
1835
|
-
rawData.timeline.forEach(t => {
|
|
1836
|
-
const total = t.tokens || 0;
|
|
1837
|
-
const cached = t.cachedTokens || 0;
|
|
1838
|
-
const cachePct = total > 0 ? ((cached / total) * 100).toFixed(1) : '0.0';
|
|
1839
|
-
const row = [
|
|
1840
|
-
t.date,
|
|
1841
|
-
total,
|
|
1842
|
-
t.promptTokens || 0,
|
|
1843
|
-
t.candidateTokens || 0,
|
|
1844
|
-
cached,
|
|
1845
|
-
cachePct,
|
|
1846
|
-
t.agent || 0,
|
|
1847
|
-
t.background || 0,
|
|
1848
|
-
t.search || 0,
|
|
1849
|
-
t.toolSuccess || 0,
|
|
1850
|
-
t.toolFailure || 0,
|
|
1851
|
-
t.toolDenied || 0,
|
|
1852
|
-
t.linesAdded || 0,
|
|
1853
|
-
t.linesRemoved || 0
|
|
1854
|
-
];
|
|
1855
|
-
csvRows.push(row.join(','));
|
|
1856
|
-
});
|
|
1857
|
-
|
|
1858
|
-
const blob = new Blob([csvRows.join('\\n')], { type: 'text/csv' });
|
|
1859
|
-
const url = URL.createObjectURL(blob);
|
|
1860
|
-
const a = document.createElement('a');
|
|
1861
|
-
a.href = url;
|
|
1862
|
-
a.download = \`fluxflow-token-usage-\${new Date().toISOString().split('T')[0]}.csv\`;
|
|
1863
|
-
a.click();
|
|
1864
|
-
URL.revokeObjectURL(url);
|
|
1865
|
-
}
|
|
1866
|
-
|
|
1867
|
-
document.querySelectorAll('#range-tabs .tab-btn').forEach(btn => {
|
|
1868
|
-
btn.addEventListener('click', (e) => {
|
|
1869
|
-
document.querySelectorAll('#range-tabs .tab-btn').forEach(b => b.classList.remove('active'));
|
|
1870
|
-
btn.classList.add('active');
|
|
1871
|
-
activeRange = btn.dataset.range;
|
|
1872
|
-
renderDashboard();
|
|
1873
|
-
});
|
|
1874
|
-
});
|
|
1875
|
-
|
|
1876
|
-
document.querySelectorAll('#table-mode-tabs .tab-btn').forEach(btn => {
|
|
1877
|
-
btn.addEventListener('click', (e) => {
|
|
1878
|
-
document.querySelectorAll('#table-mode-tabs .tab-btn').forEach(b => b.classList.remove('active'));
|
|
1879
|
-
btn.classList.add('active');
|
|
1880
|
-
tableMode = btn.dataset.tableMode;
|
|
1881
|
-
if (rawData && rawData.timeline) {
|
|
1882
|
-
renderTable(filterTimelineByRange(rawData.timeline, activeRange));
|
|
1883
|
-
}
|
|
1884
|
-
});
|
|
1885
|
-
});
|
|
1886
|
-
|
|
1887
|
-
document.querySelectorAll('.chart-btn').forEach(btn => {
|
|
1888
|
-
btn.addEventListener('click', () => {
|
|
1889
|
-
document.querySelectorAll('.chart-btn').forEach(b => b.classList.remove('active'));
|
|
1890
|
-
btn.classList.add('active');
|
|
1891
|
-
chartType = btn.dataset.chartType;
|
|
1892
|
-
if (rawData && rawData.timeline) {
|
|
1893
|
-
renderTimelineChart(filterTimelineByRange(rawData.timeline, activeRange));
|
|
1894
|
-
}
|
|
1895
|
-
});
|
|
1896
|
-
});
|
|
1897
|
-
|
|
1898
|
-
document.getElementById('btn-refresh').addEventListener('click', fetchData);
|
|
1899
|
-
document.getElementById('btn-export-json').addEventListener('click', exportJson);
|
|
1900
|
-
document.getElementById('btn-export-csv').addEventListener('click', exportCsv);
|
|
1901
|
-
document.getElementById('table-search').addEventListener('input', () => {
|
|
1902
|
-
if (rawData && rawData.timeline) {
|
|
1903
|
-
renderTable(filterTimelineByRange(rawData.timeline, activeRange));
|
|
1904
|
-
}
|
|
1905
|
-
});
|
|
1906
|
-
|
|
1907
|
-
setInterval(() => {
|
|
1908
|
-
if (document.getElementById('auto-refresh-check').checked) {
|
|
1909
|
-
fetchData();
|
|
1910
|
-
}
|
|
1911
|
-
}, 60000);
|
|
1912
|
-
|
|
1913
|
-
fetchData();
|
|
1914
|
-
</script>
|
|
1915
|
-
</body>
|
|
1916
|
-
</html>`}async function w(t=52140){if(m&&n)return{port:n,url:`http://localhost:${n}`,isNew:!1};let r=b.createServer(async(a,e)=>{if(e.setHeader("Access-Control-Allow-Origin","*"),e.setHeader("Access-Control-Allow-Methods","GET, OPTIONS"),a.method==="OPTIONS"){e.writeHead(204),e.end();return}let i=new URL(a.url,`http://${a.headers.host||"localhost"}`);if(i.pathname==="/api/usage"){try{let A=await v();e.writeHead(200,{"Content-Type":"application/json; charset=utf-8"}),e.end(JSON.stringify(A))}catch(A){e.writeHead(500,{"Content-Type":"application/json"}),e.end(JSON.stringify({error:A.message}))}return}if(i.pathname==="/"||i.pathname==="/index.html"||i.pathname==="/usage"){let A=u();e.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),e.end(A);return}e.writeHead(404,{"Content-Type":"text/plain"}),e.end("Not Found")}),o=await(a=>new Promise((e,i)=>{let A=d=>{r.once("error",c=>{c.code==="EADDRINUSE"?A(d+1):i(c)}),r.listen(d,"127.0.0.1",()=>{m=r,n=d,e(d)})};A(a)}))(t),l=`http://localhost:${o}`;return{port:o,url:l,isNew:!0}}async function D(t=52140){let{url:r,isNew:s}=await w(t),o=process.platform,l=o==="win32"?"start":o==="darwin"?"open":"xdg-open";try{p(`${l} ${r}`)}catch{}return{url:r,port:n,isNew:s}}export{w as a,D as b};
|