@autocode-cli/autocode 0.1.9 → 0.1.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/dist/cli/commands/init.js +1 -1
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/new.d.ts.map +1 -1
- package/dist/cli/commands/new.js +20 -2
- package/dist/cli/commands/new.js.map +1 -1
- package/dist/cli/commands/serve.js +1 -1
- package/dist/cli/commands/serve.js.map +1 -1
- package/dist/cli/commands/stats.d.ts +9 -0
- package/dist/cli/commands/stats.d.ts.map +1 -0
- package/dist/cli/commands/stats.js +108 -0
- package/dist/cli/commands/stats.js.map +1 -0
- package/dist/cli/parser.d.ts.map +1 -1
- package/dist/cli/parser.js +2 -0
- package/dist/cli/parser.js.map +1 -1
- package/dist/server/api.d.ts.map +1 -1
- package/dist/server/api.js +25 -13
- package/dist/server/api.js.map +1 -1
- package/dist/server/dashboard/pages/index.d.ts +1 -0
- package/dist/server/dashboard/pages/index.d.ts.map +1 -1
- package/dist/server/dashboard/pages/index.js +1 -0
- package/dist/server/dashboard/pages/index.js.map +1 -1
- package/dist/server/dashboard/pages/stats-page.d.ts +8 -0
- package/dist/server/dashboard/pages/stats-page.d.ts.map +1 -0
- package/dist/server/dashboard/pages/stats-page.js +624 -0
- package/dist/server/dashboard/pages/stats-page.js.map +1 -0
- package/dist/server/dashboard/scripts/index.d.ts.map +1 -1
- package/dist/server/dashboard/scripts/index.js +30 -1
- package/dist/server/dashboard/scripts/index.js.map +1 -1
- package/dist/server/dashboard/styles/base.d.ts.map +1 -1
- package/dist/server/dashboard/styles/base.js +9 -0
- package/dist/server/dashboard/styles/base.js.map +1 -1
- package/dist/server/dashboard.d.ts +1 -1
- package/dist/server/dashboard.d.ts.map +1 -1
- package/dist/server/dashboard.js +1 -1
- package/dist/server/dashboard.js.map +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +8 -1
- package/dist/server/index.js.map +1 -1
- package/dist/services/claude.d.ts +9 -0
- package/dist/services/claude.d.ts.map +1 -1
- package/dist/services/claude.js +105 -2
- package/dist/services/claude.js.map +1 -1
- package/dist/services/stats.d.ts +58 -0
- package/dist/services/stats.d.ts.map +1 -0
- package/dist/services/stats.js +196 -0
- package/dist/services/stats.js.map +1 -0
- package/dist/utils/fs.js +1 -1
- package/dist/utils/fs.js.map +1 -1
- package/package.json +1 -1
- package/templates/prompts/backlog.en.md +4 -1
- package/templates/prompts/backlog.fr.md +4 -1
- package/templates/prompts/changelog.en.md +6 -4
- package/templates/prompts/changelog.fr.md +6 -4
- package/templates/prompts/deploy-prod.en.md +4 -1
- package/templates/prompts/deploy-prod.fr.md +4 -1
- package/templates/prompts/deploy-staging.en.md +6 -3
- package/templates/prompts/deploy-staging.fr.md +6 -3
- package/templates/prompts/design.en.md +4 -1
- package/templates/prompts/design.fr.md +4 -1
- package/templates/prompts/dev.en.md +7 -5
- package/templates/prompts/dev.fr.md +7 -5
- package/templates/prompts/done.en.md +3 -1
- package/templates/prompts/done.fr.md +3 -1
- package/templates/prompts/git-commit.en.md +4 -1
- package/templates/prompts/git-commit.fr.md +4 -1
- package/templates/prompts/git-push.en.md +4 -1
- package/templates/prompts/git-push.fr.md +4 -1
- package/templates/prompts/git-tag.en.md +4 -1
- package/templates/prompts/git-tag.fr.md +4 -1
- package/templates/prompts/in-progress.en.md +6 -5
- package/templates/prompts/in-progress.fr.md +6 -5
- package/templates/prompts/qualification.en.md +4 -1
- package/templates/prompts/qualification.fr.md +4 -1
- package/templates/prompts/ready.en.md +6 -5
- package/templates/prompts/ready.fr.md +6 -5
- package/templates/prompts/retest-cypress.en.md +6 -4
- package/templates/prompts/retest-cypress.fr.md +6 -4
- package/templates/prompts/retest-playwright.en.md +4 -1
- package/templates/prompts/retest-playwright.fr.md +4 -1
- package/templates/prompts/retest.en.md +4 -1
- package/templates/prompts/retest.fr.md +4 -1
- package/templates/prompts/review-best-practices.en.md +6 -4
- package/templates/prompts/review-best-practices.fr.md +6 -4
- package/templates/prompts/review-code.en.md +4 -1
- package/templates/prompts/review-code.fr.md +4 -1
- package/templates/prompts/review-consistency.en.md +6 -4
- package/templates/prompts/review-consistency.fr.md +6 -4
- package/templates/prompts/review-no-duplication.en.md +6 -4
- package/templates/prompts/review-no-duplication.fr.md +6 -4
- package/templates/prompts/review-security.en.md +6 -4
- package/templates/prompts/review-security.fr.md +6 -4
- package/templates/prompts/specification.en.md +4 -1
- package/templates/prompts/specification.fr.md +4 -1
- package/templates/prompts/splitter.en.md +9 -1
- package/templates/prompts/splitter.fr.md +9 -2
- package/templates/prompts/testing-cypress.en.md +6 -4
- package/templates/prompts/testing-cypress.fr.md +6 -4
- package/templates/prompts/testing-integration.en.md +6 -4
- package/templates/prompts/testing-integration.fr.md +6 -4
- package/templates/prompts/testing-playwright.en.md +4 -1
- package/templates/prompts/testing-playwright.fr.md +4 -1
- package/templates/prompts/testing-unit.en.md +6 -4
- package/templates/prompts/testing-unit.fr.md +6 -4
- package/templates/prompts/update-docs.en.md +6 -4
- package/templates/prompts/update-docs.fr.md +6 -4
- package/templates/prompts/validate-staging.en.md +11 -9
- package/templates/prompts/validate-staging.fr.md +11 -9
|
@@ -0,0 +1,624 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stats page - Claude usage statistics dashboard
|
|
3
|
+
*/
|
|
4
|
+
import { getStyles } from '../styles/index.js';
|
|
5
|
+
/**
|
|
6
|
+
* Generate the stats page HTML
|
|
7
|
+
*/
|
|
8
|
+
export function generateStatsPage() {
|
|
9
|
+
return `<!DOCTYPE html>
|
|
10
|
+
<html lang="fr">
|
|
11
|
+
<head>
|
|
12
|
+
<meta charset="UTF-8">
|
|
13
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
14
|
+
<title>Stats - AutoCode</title>
|
|
15
|
+
<style>
|
|
16
|
+
${getStyles()}
|
|
17
|
+
|
|
18
|
+
.stats-page {
|
|
19
|
+
max-width: 1200px;
|
|
20
|
+
margin: 0 auto;
|
|
21
|
+
padding: 20px;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.stats-header {
|
|
25
|
+
display: flex;
|
|
26
|
+
justify-content: space-between;
|
|
27
|
+
align-items: center;
|
|
28
|
+
margin-bottom: 30px;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.stats-header h1 {
|
|
32
|
+
margin: 0;
|
|
33
|
+
font-size: 24px;
|
|
34
|
+
display: flex;
|
|
35
|
+
align-items: center;
|
|
36
|
+
gap: 10px;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.header-actions {
|
|
40
|
+
display: flex;
|
|
41
|
+
align-items: center;
|
|
42
|
+
gap: 12px;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.lang-switcher {
|
|
46
|
+
display: flex;
|
|
47
|
+
gap: 4px;
|
|
48
|
+
background: #252525;
|
|
49
|
+
padding: 4px;
|
|
50
|
+
border-radius: 6px;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.lang-btn {
|
|
54
|
+
padding: 4px 10px;
|
|
55
|
+
border: none;
|
|
56
|
+
background: transparent;
|
|
57
|
+
color: #888;
|
|
58
|
+
cursor: pointer;
|
|
59
|
+
border-radius: 4px;
|
|
60
|
+
font-size: 12px;
|
|
61
|
+
font-weight: 500;
|
|
62
|
+
transition: all 0.2s;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.lang-btn:hover {
|
|
66
|
+
color: #fff;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.lang-btn.active {
|
|
70
|
+
background: #a855f7;
|
|
71
|
+
color: #fff;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.refresh-indicator {
|
|
75
|
+
width: 8px;
|
|
76
|
+
height: 8px;
|
|
77
|
+
border-radius: 50%;
|
|
78
|
+
background: #333;
|
|
79
|
+
transition: all 0.3s ease;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.refresh-indicator.loading {
|
|
83
|
+
background: #a855f7;
|
|
84
|
+
animation: pulse-dot 0.5s ease;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
@keyframes pulse-dot {
|
|
88
|
+
0% { transform: scale(1); opacity: 1; }
|
|
89
|
+
50% { transform: scale(1.5); opacity: 0.7; }
|
|
90
|
+
100% { transform: scale(1); opacity: 1; }
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.stats-cards {
|
|
94
|
+
display: grid;
|
|
95
|
+
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
|
96
|
+
gap: 20px;
|
|
97
|
+
margin-bottom: 30px;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.stats-card {
|
|
101
|
+
background: #1e1e1e;
|
|
102
|
+
border: 1px solid #333;
|
|
103
|
+
border-radius: 8px;
|
|
104
|
+
padding: 20px;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.stats-card h3 {
|
|
108
|
+
margin: 0 0 15px 0;
|
|
109
|
+
font-size: 14px;
|
|
110
|
+
color: #888;
|
|
111
|
+
text-transform: uppercase;
|
|
112
|
+
letter-spacing: 1px;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.stats-card .value {
|
|
116
|
+
font-size: 32px;
|
|
117
|
+
font-weight: bold;
|
|
118
|
+
color: #fff;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.stats-card .value.small {
|
|
122
|
+
font-size: 18px;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.stats-card .label {
|
|
126
|
+
font-size: 12px;
|
|
127
|
+
color: #666;
|
|
128
|
+
margin-top: 5px;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.stats-grid {
|
|
132
|
+
display: grid;
|
|
133
|
+
grid-template-columns: 1fr 1fr;
|
|
134
|
+
gap: 15px;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.stats-item {
|
|
138
|
+
display: flex;
|
|
139
|
+
flex-direction: column;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.stats-item .value {
|
|
143
|
+
font-size: 24px;
|
|
144
|
+
font-weight: bold;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.stats-item .label {
|
|
148
|
+
font-size: 12px;
|
|
149
|
+
color: #888;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.profile-badge {
|
|
153
|
+
display: inline-flex;
|
|
154
|
+
align-items: center;
|
|
155
|
+
gap: 8px;
|
|
156
|
+
padding: 8px 12px;
|
|
157
|
+
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
158
|
+
border-radius: 20px;
|
|
159
|
+
font-weight: 500;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.model-badge {
|
|
163
|
+
display: inline-block;
|
|
164
|
+
padding: 4px 8px;
|
|
165
|
+
background: #333;
|
|
166
|
+
border-radius: 4px;
|
|
167
|
+
font-family: monospace;
|
|
168
|
+
font-size: 14px;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
.history-table {
|
|
172
|
+
width: 100%;
|
|
173
|
+
border-collapse: collapse;
|
|
174
|
+
margin-top: 10px;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.history-table th,
|
|
178
|
+
.history-table td {
|
|
179
|
+
padding: 12px;
|
|
180
|
+
text-align: left;
|
|
181
|
+
border-bottom: 1px solid #333;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
.history-table th {
|
|
185
|
+
color: #888;
|
|
186
|
+
font-weight: 500;
|
|
187
|
+
font-size: 12px;
|
|
188
|
+
text-transform: uppercase;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.history-table tr:hover {
|
|
192
|
+
background: #252525;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.history-table .ticket-link {
|
|
196
|
+
color: #a855f7;
|
|
197
|
+
text-decoration: none;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
.history-table .ticket-link:hover {
|
|
201
|
+
text-decoration: underline;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
.tokens-badge {
|
|
205
|
+
display: inline-flex;
|
|
206
|
+
gap: 8px;
|
|
207
|
+
font-size: 13px;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.tokens-badge .in {
|
|
211
|
+
color: #22c55e;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
.tokens-badge .out {
|
|
215
|
+
color: #3b82f6;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
.empty-state {
|
|
219
|
+
text-align: center;
|
|
220
|
+
padding: 40px;
|
|
221
|
+
color: #666;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
.info-box {
|
|
225
|
+
display: flex;
|
|
226
|
+
gap: 12px;
|
|
227
|
+
margin-top: 20px;
|
|
228
|
+
padding: 16px;
|
|
229
|
+
background: rgba(59, 130, 246, 0.1);
|
|
230
|
+
border: 1px solid rgba(59, 130, 246, 0.3);
|
|
231
|
+
border-radius: 8px;
|
|
232
|
+
font-size: 13px;
|
|
233
|
+
line-height: 1.5;
|
|
234
|
+
color: #94a3b8;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
.info-box .icon {
|
|
238
|
+
flex-shrink: 0;
|
|
239
|
+
width: 20px;
|
|
240
|
+
height: 20px;
|
|
241
|
+
display: flex;
|
|
242
|
+
align-items: center;
|
|
243
|
+
justify-content: center;
|
|
244
|
+
background: #3b82f6;
|
|
245
|
+
color: #fff;
|
|
246
|
+
border-radius: 50%;
|
|
247
|
+
font-size: 12px;
|
|
248
|
+
font-weight: bold;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
.info-box strong {
|
|
252
|
+
color: #e2e8f0;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
.info-box code {
|
|
256
|
+
background: rgba(0,0,0,0.3);
|
|
257
|
+
padding: 2px 6px;
|
|
258
|
+
border-radius: 4px;
|
|
259
|
+
font-family: monospace;
|
|
260
|
+
color: #22c55e;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
.btn-back {
|
|
264
|
+
display: inline-flex;
|
|
265
|
+
align-items: center;
|
|
266
|
+
gap: 8px;
|
|
267
|
+
padding: 8px 16px;
|
|
268
|
+
background: #333;
|
|
269
|
+
color: #fff;
|
|
270
|
+
border: none;
|
|
271
|
+
border-radius: 6px;
|
|
272
|
+
text-decoration: none;
|
|
273
|
+
font-size: 14px;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
.btn-back:hover {
|
|
277
|
+
background: #444;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
.processing-indicator {
|
|
281
|
+
display: inline-flex;
|
|
282
|
+
align-items: center;
|
|
283
|
+
gap: 8px;
|
|
284
|
+
padding: 4px 8px;
|
|
285
|
+
background: rgba(168, 85, 247, 0.2);
|
|
286
|
+
border-radius: 4px;
|
|
287
|
+
font-size: 12px;
|
|
288
|
+
color: #a855f7;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
.processing-indicator::before {
|
|
292
|
+
content: '';
|
|
293
|
+
width: 8px;
|
|
294
|
+
height: 8px;
|
|
295
|
+
border-radius: 50%;
|
|
296
|
+
background: #a855f7;
|
|
297
|
+
animation: pulse 1.5s infinite;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
@keyframes pulse {
|
|
301
|
+
0%, 100% { opacity: 1; }
|
|
302
|
+
50% { opacity: 0.5; }
|
|
303
|
+
}
|
|
304
|
+
</style>
|
|
305
|
+
</head>
|
|
306
|
+
<body>
|
|
307
|
+
<div class="stats-page">
|
|
308
|
+
<div class="stats-header">
|
|
309
|
+
<h1><span data-i18n="title"></span> <span id="refresh-indicator" class="refresh-indicator"></span></h1>
|
|
310
|
+
<div class="header-actions">
|
|
311
|
+
<div class="lang-switcher">
|
|
312
|
+
<button class="lang-btn" data-lang="fr">FR</button>
|
|
313
|
+
<button class="lang-btn" data-lang="en">EN</button>
|
|
314
|
+
</div>
|
|
315
|
+
<a href="/" class="btn-back" data-i18n="back"></a>
|
|
316
|
+
</div>
|
|
317
|
+
</div>
|
|
318
|
+
|
|
319
|
+
<div class="stats-cards">
|
|
320
|
+
<div class="stats-card">
|
|
321
|
+
<h3 data-i18n="profile"></h3>
|
|
322
|
+
<div class="profile-badge" id="profile">-</div>
|
|
323
|
+
<div style="margin-top: 15px">
|
|
324
|
+
<span class="model-badge" id="model">-</span>
|
|
325
|
+
</div>
|
|
326
|
+
</div>
|
|
327
|
+
|
|
328
|
+
<div class="stats-card">
|
|
329
|
+
<h3 data-i18n="session"></h3>
|
|
330
|
+
<div class="stats-grid">
|
|
331
|
+
<div class="stats-item">
|
|
332
|
+
<span class="value" id="session-tokens">-</span>
|
|
333
|
+
<span class="label" data-i18n="totalTokens"></span>
|
|
334
|
+
</div>
|
|
335
|
+
<div class="stats-item">
|
|
336
|
+
<span class="value" id="session-calls">-</span>
|
|
337
|
+
<span class="label" data-i18n="calls"></span>
|
|
338
|
+
</div>
|
|
339
|
+
<div class="stats-item">
|
|
340
|
+
<span class="value" id="session-cost" style="color: #22c55e;">-</span>
|
|
341
|
+
<span class="label" data-i18n="cost"></span>
|
|
342
|
+
</div>
|
|
343
|
+
</div>
|
|
344
|
+
<div class="label" id="session-started" style="margin-top: 10px;"></div>
|
|
345
|
+
</div>
|
|
346
|
+
|
|
347
|
+
<div class="stats-card">
|
|
348
|
+
<h3 data-i18n="totals"></h3>
|
|
349
|
+
<div class="stats-grid">
|
|
350
|
+
<div class="stats-item">
|
|
351
|
+
<span class="value" id="total-tokens">-</span>
|
|
352
|
+
<span class="label" data-i18n="totalTokens"></span>
|
|
353
|
+
</div>
|
|
354
|
+
<div class="stats-item">
|
|
355
|
+
<span class="value" id="total-calls">-</span>
|
|
356
|
+
<span class="label" data-i18n="calls"></span>
|
|
357
|
+
</div>
|
|
358
|
+
<div class="stats-item">
|
|
359
|
+
<span class="value" id="total-cost" style="color: #22c55e;">-</span>
|
|
360
|
+
<span class="label" data-i18n="cost"></span>
|
|
361
|
+
</div>
|
|
362
|
+
</div>
|
|
363
|
+
</div>
|
|
364
|
+
|
|
365
|
+
<div class="stats-card" id="processing-card" style="display: none;">
|
|
366
|
+
<h3 data-i18n="processing"></h3>
|
|
367
|
+
<div id="processing-tickets"></div>
|
|
368
|
+
</div>
|
|
369
|
+
</div>
|
|
370
|
+
|
|
371
|
+
<div class="stats-card">
|
|
372
|
+
<h3 data-i18n="history"></h3>
|
|
373
|
+
<div id="history-container">
|
|
374
|
+
<div class="empty-state" data-i18n="loading"></div>
|
|
375
|
+
</div>
|
|
376
|
+
<div class="info-box">
|
|
377
|
+
<span class="icon">i</span>
|
|
378
|
+
<div id="cost-explanation"></div>
|
|
379
|
+
</div>
|
|
380
|
+
</div>
|
|
381
|
+
</div>
|
|
382
|
+
|
|
383
|
+
<script>
|
|
384
|
+
// ========================================
|
|
385
|
+
// TRANSLATIONS
|
|
386
|
+
// ========================================
|
|
387
|
+
const translations = {
|
|
388
|
+
fr: {
|
|
389
|
+
title: 'Statistiques Claude',
|
|
390
|
+
profile: 'Profil',
|
|
391
|
+
model: 'Modèle',
|
|
392
|
+
session: 'Session actuelle',
|
|
393
|
+
totals: 'Totaux',
|
|
394
|
+
tokensInput: 'Tokens entrée',
|
|
395
|
+
tokensOutput: 'Tokens sortie',
|
|
396
|
+
totalTokens: 'Total tokens',
|
|
397
|
+
calls: 'Appels',
|
|
398
|
+
cost: 'Coût',
|
|
399
|
+
history: 'Historique des appels',
|
|
400
|
+
noHistory: 'Aucun appel enregistré',
|
|
401
|
+
back: 'Retour au dashboard',
|
|
402
|
+
startedAt: 'Démarré le',
|
|
403
|
+
ticket: 'Ticket',
|
|
404
|
+
column: 'Colonne',
|
|
405
|
+
duration: 'Durée',
|
|
406
|
+
date: 'Date',
|
|
407
|
+
loading: 'Chargement...',
|
|
408
|
+
processing: 'En cours',
|
|
409
|
+
costExplanation: '<strong>Pourquoi le coût semble bas ?</strong> Claude utilise un <strong>cache de prompts</strong> qui réduit significativement les coûts. Quand une partie du contexte a déjà été envoyée dans une conversation précédente, Claude ne la refacture pas au prix plein. Le coût affiché est le <strong>coût réel facturé</strong> par l\\'API Claude, qui inclut ces réductions de cache. C\\'est pour cela que <code>tokens × prix</code> ne correspond pas toujours au coût affiché - et c\\'est une bonne nouvelle pour votre portefeuille !',
|
|
410
|
+
},
|
|
411
|
+
en: {
|
|
412
|
+
title: 'Claude Statistics',
|
|
413
|
+
profile: 'Profile',
|
|
414
|
+
model: 'Model',
|
|
415
|
+
session: 'Current session',
|
|
416
|
+
totals: 'Totals',
|
|
417
|
+
tokensInput: 'Input tokens',
|
|
418
|
+
tokensOutput: 'Output tokens',
|
|
419
|
+
totalTokens: 'Total tokens',
|
|
420
|
+
calls: 'Calls',
|
|
421
|
+
cost: 'Cost',
|
|
422
|
+
history: 'Call history',
|
|
423
|
+
noHistory: 'No calls recorded',
|
|
424
|
+
back: 'Back to dashboard',
|
|
425
|
+
startedAt: 'Started at',
|
|
426
|
+
ticket: 'Ticket',
|
|
427
|
+
column: 'Column',
|
|
428
|
+
duration: 'Duration',
|
|
429
|
+
date: 'Date',
|
|
430
|
+
loading: 'Loading...',
|
|
431
|
+
processing: 'Processing',
|
|
432
|
+
costExplanation: '<strong>Why does the cost seem low?</strong> Claude uses <strong>prompt caching</strong> which significantly reduces costs. When part of the context was already sent in a previous conversation, Claude doesn\\'t charge full price again. The cost shown is the <strong>actual cost billed</strong> by the Claude API, including these cache discounts. That\\'s why <code>tokens × price</code> doesn\\'t always match the displayed cost - and that\\'s good news for your wallet!',
|
|
433
|
+
}
|
|
434
|
+
};
|
|
435
|
+
|
|
436
|
+
let currentLang = localStorage.getItem('autocode-lang') || 'fr';
|
|
437
|
+
let statsData = null;
|
|
438
|
+
|
|
439
|
+
function t(key) {
|
|
440
|
+
return translations[currentLang][key] || translations['en'][key] || key;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
function switchLanguage(lang) {
|
|
444
|
+
currentLang = lang;
|
|
445
|
+
localStorage.setItem('autocode-lang', lang);
|
|
446
|
+
document.documentElement.lang = lang;
|
|
447
|
+
|
|
448
|
+
// Update lang switcher buttons
|
|
449
|
+
document.querySelectorAll('.lang-switcher .lang-btn').forEach(btn => {
|
|
450
|
+
btn.classList.toggle('active', btn.dataset.lang === lang);
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
// Update all elements with data-i18n
|
|
454
|
+
document.querySelectorAll('[data-i18n]').forEach(el => {
|
|
455
|
+
const key = el.getAttribute('data-i18n');
|
|
456
|
+
if (translations[lang] && translations[lang][key]) {
|
|
457
|
+
el.textContent = translations[lang][key];
|
|
458
|
+
}
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
// Update cost explanation (HTML content)
|
|
462
|
+
document.getElementById('cost-explanation').innerHTML = t('costExplanation');
|
|
463
|
+
|
|
464
|
+
// Update page title
|
|
465
|
+
document.title = t('title') + ' - AutoCode';
|
|
466
|
+
|
|
467
|
+
// Re-render history table if data exists
|
|
468
|
+
if (statsData) {
|
|
469
|
+
renderHistory(statsData);
|
|
470
|
+
renderSessionStarted(statsData);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// ========================================
|
|
475
|
+
// FORMATTING
|
|
476
|
+
// ========================================
|
|
477
|
+
function formatNumber(n) {
|
|
478
|
+
if (n >= 1000000) return (n / 1000000).toFixed(1) + 'M';
|
|
479
|
+
if (n >= 1000) return (n / 1000).toFixed(1) + 'K';
|
|
480
|
+
return n.toString();
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
function formatDate(iso) {
|
|
484
|
+
const d = new Date(iso);
|
|
485
|
+
return d.toLocaleString(currentLang === 'fr' ? 'fr-FR' : 'en-US');
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
function formatDuration(ms) {
|
|
489
|
+
if (ms < 1000) return ms + 'ms';
|
|
490
|
+
return (ms / 1000).toFixed(1) + 's';
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// ========================================
|
|
494
|
+
// RENDERING
|
|
495
|
+
// ========================================
|
|
496
|
+
function renderSessionStarted(data) {
|
|
497
|
+
if (data.session.startedAt) {
|
|
498
|
+
document.getElementById('session-started').textContent = t('startedAt') + ': ' + formatDate(data.session.startedAt);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
function renderHistory(data) {
|
|
503
|
+
if (data.history && data.history.length > 0) {
|
|
504
|
+
document.getElementById('history-container').innerHTML = \`
|
|
505
|
+
<table class="history-table">
|
|
506
|
+
<colgroup>
|
|
507
|
+
<col style="width: 100px;">
|
|
508
|
+
<col style="width: 200px;">
|
|
509
|
+
<col style="width: 100px;">
|
|
510
|
+
<col style="width: 100px;">
|
|
511
|
+
<col style="width: 80px;">
|
|
512
|
+
<col style="width: 70px;">
|
|
513
|
+
<col style="width: 160px;">
|
|
514
|
+
</colgroup>
|
|
515
|
+
<thead>
|
|
516
|
+
<tr>
|
|
517
|
+
<th>\${t('ticket')}</th>
|
|
518
|
+
<th>\${t('column')}</th>
|
|
519
|
+
<th>\${t('tokensInput')}</th>
|
|
520
|
+
<th>\${t('tokensOutput')}</th>
|
|
521
|
+
<th>\${t('cost')}</th>
|
|
522
|
+
<th>\${t('duration')}</th>
|
|
523
|
+
<th>\${t('date')}</th>
|
|
524
|
+
</tr>
|
|
525
|
+
</thead>
|
|
526
|
+
<tbody>
|
|
527
|
+
\${data.history.map(h => \`
|
|
528
|
+
<tr>
|
|
529
|
+
<td>\${h.ticket ? '<a href="/ticket/' + h.ticket + '" class="ticket-link">' + h.ticket + '</a>' : '-'}</td>
|
|
530
|
+
<td>\${h.column || '-'}</td>
|
|
531
|
+
<td>\${formatNumber(h.tokensInput || 0)}</td>
|
|
532
|
+
<td>\${formatNumber(h.tokensOutput || 0)}</td>
|
|
533
|
+
<td style="color: #22c55e;">\${h.costUsd ? '$' + h.costUsd.toFixed(4) : '-'}</td>
|
|
534
|
+
<td>\${formatDuration(h.duration)}</td>
|
|
535
|
+
<td>\${formatDate(h.timestamp)}</td>
|
|
536
|
+
</tr>
|
|
537
|
+
\`).join('')}
|
|
538
|
+
</tbody>
|
|
539
|
+
</table>
|
|
540
|
+
\`;
|
|
541
|
+
} else {
|
|
542
|
+
document.getElementById('history-container').innerHTML = '<div class="empty-state">' + t('noHistory') + '</div>';
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
// ========================================
|
|
547
|
+
// DATA LOADING
|
|
548
|
+
// ========================================
|
|
549
|
+
async function loadStats() {
|
|
550
|
+
const indicator = document.getElementById('refresh-indicator');
|
|
551
|
+
indicator.classList.add('loading');
|
|
552
|
+
|
|
553
|
+
try {
|
|
554
|
+
const res = await fetch('/api/stats');
|
|
555
|
+
const json = await res.json();
|
|
556
|
+
|
|
557
|
+
if (!json.success) throw new Error(json.error);
|
|
558
|
+
|
|
559
|
+
statsData = json.data;
|
|
560
|
+
|
|
561
|
+
// Profile & Model
|
|
562
|
+
document.getElementById('profile').textContent = statsData.profile || 'default';
|
|
563
|
+
document.getElementById('model').textContent = statsData.model || 'N/A';
|
|
564
|
+
|
|
565
|
+
// Session
|
|
566
|
+
const sessionTotal = (statsData.session.tokensInput || 0) + (statsData.session.tokensOutput || 0);
|
|
567
|
+
document.getElementById('session-tokens').textContent = formatNumber(sessionTotal);
|
|
568
|
+
document.getElementById('session-calls').textContent = statsData.session.callCount || 0;
|
|
569
|
+
document.getElementById('session-cost').textContent = '$' + (statsData.session.costUsd || 0).toFixed(2);
|
|
570
|
+
renderSessionStarted(statsData);
|
|
571
|
+
|
|
572
|
+
// Totals
|
|
573
|
+
const totalTokens = (statsData.totals.tokensInput || 0) + (statsData.totals.tokensOutput || 0);
|
|
574
|
+
document.getElementById('total-tokens').textContent = formatNumber(totalTokens);
|
|
575
|
+
document.getElementById('total-calls').textContent = statsData.totals.callCount || 0;
|
|
576
|
+
document.getElementById('total-cost').textContent = '$' + (statsData.totals.costUsd || 0).toFixed(2);
|
|
577
|
+
|
|
578
|
+
// Processing tickets
|
|
579
|
+
if (statsData.processingTickets && statsData.processingTickets.length > 0) {
|
|
580
|
+
document.getElementById('processing-card').style.display = 'block';
|
|
581
|
+
document.getElementById('processing-tickets').innerHTML = statsData.processingTickets
|
|
582
|
+
.map(ticket => '<div class="processing-indicator">' + ticket + '</div>')
|
|
583
|
+
.join('');
|
|
584
|
+
} else {
|
|
585
|
+
document.getElementById('processing-card').style.display = 'none';
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
// History
|
|
589
|
+
renderHistory(statsData);
|
|
590
|
+
} catch (e) {
|
|
591
|
+
console.error('Failed to load stats:', e);
|
|
592
|
+
} finally {
|
|
593
|
+
setTimeout(() => indicator.classList.remove('loading'), 500);
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
// ========================================
|
|
598
|
+
// INIT
|
|
599
|
+
// ========================================
|
|
600
|
+
// Initialize language switcher
|
|
601
|
+
document.querySelectorAll('.lang-switcher .lang-btn').forEach(btn => {
|
|
602
|
+
btn.addEventListener('click', () => switchLanguage(btn.dataset.lang));
|
|
603
|
+
});
|
|
604
|
+
switchLanguage(currentLang);
|
|
605
|
+
|
|
606
|
+
// Initial load
|
|
607
|
+
loadStats();
|
|
608
|
+
|
|
609
|
+
// Auto-refresh every 5 seconds
|
|
610
|
+
setInterval(loadStats, 5000);
|
|
611
|
+
|
|
612
|
+
// WebSocket for real-time updates
|
|
613
|
+
const ws = new WebSocket('ws://' + location.host + '/ws');
|
|
614
|
+
ws.onmessage = function(event) {
|
|
615
|
+
const data = JSON.parse(event.data);
|
|
616
|
+
if (data.type === 'claude_end') {
|
|
617
|
+
loadStats();
|
|
618
|
+
}
|
|
619
|
+
};
|
|
620
|
+
</script>
|
|
621
|
+
</body>
|
|
622
|
+
</html>`;
|
|
623
|
+
}
|
|
624
|
+
//# sourceMappingURL=stats-page.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stats-page.js","sourceRoot":"","sources":["../../../../src/server/dashboard/pages/stats-page.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAE/C;;GAEG;AACH,MAAM,UAAU,iBAAiB;IAC/B,OAAO;;;;;;;MAOH,SAAS,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QA8lBT,CAAC;AACT,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/server/dashboard/scripts/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;GAEG;AACH,wBAAgB,SAAS,IAAI,MAAM,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/server/dashboard/scripts/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;GAEG;AACH,wBAAgB,SAAS,IAAI,MAAM,CA+zClC"}
|