claude-usage-dashboard 1.3.14 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/cli.cjs CHANGED
@@ -1,8 +1,20 @@
1
1
  #!/usr/bin/env node
2
2
  'use strict';
3
- const { join } = require('path');
3
+ const { join, resolve } = require('path');
4
4
  const { spawnSync } = require('child_process');
5
5
 
6
+ // Parse CLI args before spawning server
7
+ const args = process.argv.slice(2);
8
+ for (let i = 0; i < args.length; i++) {
9
+ if (args[i] === '--sync-dir' && args[i + 1]) {
10
+ process.env.CLAUDE_DASH_SYNC_DIR = resolve(args[i + 1]);
11
+ i++;
12
+ } else if (args[i] === '--machine-name' && args[i + 1]) {
13
+ process.env.CLAUDE_DASH_MACHINE_NAME = args[i + 1];
14
+ i++;
15
+ }
16
+ }
17
+
6
18
  const serverPath = join(__dirname, '..', 'server', 'index.js');
7
19
  const result = spawnSync(process.execPath, [serverPath], { stdio: 'inherit' });
8
20
  process.exit(result.status || 0);
package/bin/cli.js CHANGED
@@ -1,2 +1,16 @@
1
- #!/usr/bin/env node
2
- import '../server/index.js';
1
+ #!/usr/bin/env node
2
+ import path from 'path';
3
+
4
+ // Parse CLI args before importing server
5
+ const args = process.argv.slice(2);
6
+ for (let i = 0; i < args.length; i++) {
7
+ if (args[i] === '--sync-dir' && args[i + 1]) {
8
+ process.env.CLAUDE_DASH_SYNC_DIR = path.resolve(args[i + 1]);
9
+ i++;
10
+ } else if (args[i] === '--machine-name' && args[i + 1]) {
11
+ process.env.CLAUDE_DASH_MACHINE_NAME = args[i + 1];
12
+ i++;
13
+ }
14
+ }
15
+
16
+ await import('../server/index.js');
package/bin/cli.sh CHANGED
@@ -1,11 +1,11 @@
1
- #!/bin/sh
2
- # Resolve symlinks to find the real script location
3
- SCRIPT="$0"
4
- while [ -L "$SCRIPT" ]; do
5
- DIR="$(cd -P "$(dirname "$SCRIPT")" && pwd)"
6
- SCRIPT="$(readlink "$SCRIPT")"
7
- case "$SCRIPT" in /*) ;; *) SCRIPT="$DIR/$SCRIPT" ;; esac
8
- done
9
- DIR="$(cd -P "$(dirname "$SCRIPT")" && pwd)"
10
-
11
- exec node "$DIR/../server/index.js" "$@"
1
+ #!/bin/sh
2
+ # Resolve symlinks to find the real script location
3
+ SCRIPT="$0"
4
+ while [ -L "$SCRIPT" ]; do
5
+ DIR="$(cd -P "$(dirname "$SCRIPT")" && pwd)"
6
+ SCRIPT="$(readlink "$SCRIPT")"
7
+ case "$SCRIPT" in /*) ;; *) SCRIPT="$DIR/$SCRIPT" ;; esac
8
+ done
9
+ DIR="$(cd -P "$(dirname "$SCRIPT")" && pwd)"
10
+
11
+ exec node "$DIR/../server/index.js" "$@"
package/package.json CHANGED
@@ -1,40 +1,43 @@
1
- {
2
- "name": "claude-usage-dashboard",
3
- "version": "1.3.14",
4
- "description": "Dashboard that visualizes Claude Code usage from local session logs",
5
- "main": "server/index.js",
6
- "bin": {
7
- "claude-usage-dashboard": "bin/cli.cjs"
8
- },
9
- "files": [
10
- "bin/",
11
- "server/",
12
- "public/"
13
- ],
14
- "scripts": {
15
- "start": "node server/index.js",
16
- "test": "mocha test/**/*.test.js --timeout 5000"
17
- },
18
- "keywords": [
19
- "claude",
20
- "usage",
21
- "dashboard",
22
- "token",
23
- "cost"
24
- ],
25
- "author": "",
26
- "license": "ISC",
27
- "repository": {
28
- "type": "git",
29
- "url": "https://github.com/ludengz/claude-usage-dashboard.git"
30
- },
31
- "type": "module",
32
- "dependencies": {
33
- "d3": "^7.9.0",
34
- "express": "^5.2.1"
35
- },
36
- "devDependencies": {
37
- "chai": "^6.2.2",
38
- "mocha": "^11.7.5"
39
- }
40
- }
1
+ {
2
+ "name": "claude-usage-dashboard",
3
+ "version": "1.4.0",
4
+ "description": "Dashboard that visualizes Claude Code usage from local session logs",
5
+ "main": "server/index.js",
6
+ "bin": {
7
+ "claude-usage-dashboard": "bin/cli.cjs"
8
+ },
9
+ "files": [
10
+ "bin/",
11
+ "server/",
12
+ "public/"
13
+ ],
14
+ "scripts": {
15
+ "start": "node server/index.js",
16
+ "test": "mocha test/**/*.test.js --timeout 5000"
17
+ },
18
+ "keywords": [
19
+ "claude",
20
+ "usage",
21
+ "dashboard",
22
+ "token",
23
+ "cost"
24
+ ],
25
+ "author": "",
26
+ "license": "ISC",
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "https://github.com/ludengz/claude-usage-dashboard.git"
30
+ },
31
+ "type": "module",
32
+ "engines": {
33
+ "node": ">=18.0.0"
34
+ },
35
+ "dependencies": {
36
+ "d3": "^7.9.0",
37
+ "express": "^5.2.1"
38
+ },
39
+ "devDependencies": {
40
+ "chai": "^6.2.2",
41
+ "mocha": "^11.7.5"
42
+ }
43
+ }
@@ -1,265 +1,265 @@
1
- * { margin: 0; padding: 0; box-sizing: border-box; }
2
-
3
- :root {
4
- --bg-primary: #0f172a;
5
- --bg-card: #1e293b;
6
- --bg-input: #334155;
7
- --border: #475569;
8
- --text-primary: #f8fafc;
9
- --text-secondary: #94a3b8;
10
- --text-muted: #64748b;
11
- --blue: #3b82f6;
12
- --blue-light: #60a5fa;
13
- --purple: #8b5cf6;
14
- --orange: #f97316;
15
- --amber: #f59e0b;
16
- --green: #4ade80;
17
- --red: #ef4444;
18
- }
19
-
20
- body {
21
- background: var(--bg-primary);
22
- color: var(--text-primary);
23
- font-family: system-ui, -apple-system, sans-serif;
24
- padding: 0 24px 40px;
25
- }
26
-
27
- .top-bar {
28
- display: flex;
29
- justify-content: space-between;
30
- align-items: center;
31
- padding: 16px 0;
32
- border-bottom: 1px solid var(--bg-card);
33
- margin-bottom: 20px;
34
- }
35
- .logo { font-size: 18px; font-weight: 700; display: flex; align-items: center; gap: 8px; }
36
- .claude-icon { width: 22px; height: 22px; flex-shrink: 0; }
37
- .controls { display: flex; gap: 12px; align-items: center; }
38
-
39
- .summary-cards {
40
- display: grid;
41
- grid-template-columns: repeat(3, 1fr);
42
- gap: 12px;
43
- margin-bottom: 20px;
44
- }
45
- .card {
46
- background: var(--bg-card);
47
- border-radius: 8px;
48
- padding: 16px;
49
- }
50
- .card-label {
51
- font-size: 11px;
52
- color: var(--text-muted);
53
- text-transform: uppercase;
54
- letter-spacing: 1px;
55
- }
56
- .card-value {
57
- font-size: 24px;
58
- font-weight: 700;
59
- margin-top: 4px;
60
- }
61
- .card-sub {
62
- font-size: 11px;
63
- color: var(--text-secondary);
64
- margin-top: 2px;
65
- }
66
- #val-api-cost { color: var(--amber); }
67
- #val-savings { color: var(--green); }
68
- #val-cache-rate { color: var(--blue-light); }
69
-
70
- .chart-section {
71
- background: var(--bg-card);
72
- border-radius: 8px;
73
- padding: 20px;
74
- margin-bottom: 12px;
75
- }
76
- .chart-section h2 {
77
- font-size: 14px;
78
- font-weight: 600;
79
- margin-bottom: 16px;
80
- }
81
- .chart-header {
82
- display: flex;
83
- justify-content: space-between;
84
- align-items: center;
85
- margin-bottom: 16px;
86
- }
87
- .chart-header h2 { margin-bottom: 0; }
88
- .chart-container { min-height: 200px; }
89
-
90
- .chart-row-3 {
91
- display: grid;
92
- grid-template-columns: repeat(3, 1fr);
93
- gap: 12px;
94
- margin-bottom: 12px;
95
- }
96
-
97
- .granularity-toggle { display: flex; gap: 4px; }
98
- .granularity-toggle button {
99
- padding: 4px 12px;
100
- background: var(--bg-input);
101
- border: none;
102
- border-radius: 4px;
103
- color: var(--text-secondary);
104
- font-size: 12px;
105
- cursor: pointer;
106
- }
107
- .granularity-toggle button.active {
108
- background: var(--blue);
109
- color: white;
110
- box-shadow: 0 0 0 1px rgba(59,130,246,0.5);
111
- }
112
- .granularity-toggle button:disabled {
113
- opacity: 0.35;
114
- cursor: not-allowed;
115
- }
116
-
117
- .table-controls { display: flex; gap: 8px; }
118
- .filter-input, .sort-select {
119
- padding: 6px 10px;
120
- background: var(--bg-input);
121
- border: 1px solid var(--border);
122
- border-radius: 6px;
123
- color: var(--text-primary);
124
- font-size: 12px;
125
- }
126
- .filter-input { width: 180px; }
127
-
128
- .table-container { overflow-x: auto; }
129
- .table-container table {
130
- width: 100%;
131
- border-collapse: collapse;
132
- font-size: 12px;
133
- }
134
- .table-container th {
135
- text-align: left;
136
- padding: 10px 8px;
137
- border-bottom: 2px solid var(--bg-input);
138
- color: var(--text-muted);
139
- text-transform: uppercase;
140
- font-size: 10px;
141
- letter-spacing: 1px;
142
- cursor: pointer;
143
- }
144
- .table-container th.align-right,
145
- .table-container td.align-right { text-align: right; }
146
- .table-container td {
147
- padding: 10px 8px;
148
- border-bottom: 1px solid var(--bg-primary);
149
- color: var(--text-secondary);
150
- }
151
- .table-container tfoot td {
152
- border-top: 2px solid var(--bg-input);
153
- font-weight: 600;
154
- color: var(--text-primary);
155
- }
156
-
157
- .tag {
158
- display: inline-block;
159
- padding: 2px 8px;
160
- border-radius: 4px;
161
- font-size: 11px;
162
- }
163
- .tag-project { background: #1e3a5f; color: var(--blue-light); }
164
- .tag-model-sonnet { background: #1e3a5f; color: var(--blue-light); }
165
- .tag-model-opus { background: #3b1764; color: #c084fc; }
166
- .tag-model-haiku { background: #1a2e1a; color: var(--green); }
167
-
168
- .pagination {
169
- display: flex;
170
- justify-content: center;
171
- gap: 4px;
172
- margin-top: 12px;
173
- }
174
- .pagination button {
175
- padding: 4px 10px;
176
- background: var(--bg-input);
177
- border: none;
178
- border-radius: 4px;
179
- font-size: 11px;
180
- color: var(--text-secondary);
181
- cursor: pointer;
182
- }
183
- .pagination button.active {
184
- background: var(--blue);
185
- color: white;
186
- }
187
-
188
- .date-picker {
189
- display: flex;
190
- align-items: center;
191
- gap: 8px;
192
- }
193
- .date-picker input {
194
- padding: 6px 10px;
195
- background: var(--bg-input);
196
- border: 1px solid var(--border);
197
- border-radius: 6px;
198
- color: var(--text-primary);
199
- font-size: 12px;
200
- }
201
- .date-picker span { color: var(--text-secondary); font-size: 12px; }
202
-
203
- .refresh-controls {
204
- display: flex;
205
- align-items: center;
206
- gap: 8px;
207
- }
208
- .btn-refresh {
209
- padding: 4px 10px;
210
- background: var(--bg-input);
211
- border: 1px solid var(--border);
212
- border-radius: 6px;
213
- color: var(--text-primary);
214
- font-size: 16px;
215
- cursor: pointer;
216
- line-height: 1;
217
- }
218
- .btn-refresh:hover { background: var(--blue); }
219
- .auto-refresh-label {
220
- display: flex;
221
- align-items: center;
222
- gap: 4px;
223
- font-size: 12px;
224
- color: var(--text-secondary);
225
- cursor: pointer;
226
- }
227
- .last-updated {
228
- font-size: 11px;
229
- color: var(--text-muted);
230
- white-space: nowrap;
231
- }
232
-
233
- .plan-selector select, .plan-selector input {
234
- padding: 6px 10px;
235
- background: var(--bg-input);
236
- border: 1px solid var(--border);
237
- border-radius: 6px;
238
- color: var(--text-primary);
239
- font-size: 12px;
240
- }
241
-
242
- .d3-tooltip {
243
- position: absolute;
244
- padding: 8px 12px;
245
- background: rgba(15, 23, 42, 0.95);
246
- border: 1px solid var(--border);
247
- border-radius: 6px;
248
- font-size: 12px;
249
- color: var(--text-primary);
250
- pointer-events: none;
251
- z-index: 100;
252
- }
253
-
254
- .quota-unavailable {
255
- color: var(--text-muted);
256
- font-size: 13px;
257
- padding: 20px;
258
- text-align: center;
259
- }
260
- #quota-section .chart-container { min-height: auto; }
261
-
262
- @media (max-width: 768px) {
263
- .summary-cards { grid-template-columns: repeat(2, 1fr); }
264
- .chart-row-3 { grid-template-columns: 1fr; }
265
- }
1
+ * { margin: 0; padding: 0; box-sizing: border-box; }
2
+
3
+ :root {
4
+ --bg-primary: #0f172a;
5
+ --bg-card: #1e293b;
6
+ --bg-input: #334155;
7
+ --border: #475569;
8
+ --text-primary: #f8fafc;
9
+ --text-secondary: #94a3b8;
10
+ --text-muted: #64748b;
11
+ --blue: #3b82f6;
12
+ --blue-light: #60a5fa;
13
+ --purple: #8b5cf6;
14
+ --orange: #f97316;
15
+ --amber: #f59e0b;
16
+ --green: #4ade80;
17
+ --red: #ef4444;
18
+ }
19
+
20
+ body {
21
+ background: var(--bg-primary);
22
+ color: var(--text-primary);
23
+ font-family: system-ui, -apple-system, sans-serif;
24
+ padding: 0 24px 40px;
25
+ }
26
+
27
+ .top-bar {
28
+ display: flex;
29
+ justify-content: space-between;
30
+ align-items: center;
31
+ padding: 16px 0;
32
+ border-bottom: 1px solid var(--bg-card);
33
+ margin-bottom: 20px;
34
+ }
35
+ .logo { font-size: 18px; font-weight: 700; display: flex; align-items: center; gap: 8px; }
36
+ .claude-icon { width: 22px; height: 22px; flex-shrink: 0; }
37
+ .controls { display: flex; gap: 12px; align-items: center; }
38
+
39
+ .summary-cards {
40
+ display: grid;
41
+ grid-template-columns: repeat(3, 1fr);
42
+ gap: 12px;
43
+ margin-bottom: 20px;
44
+ }
45
+ .card {
46
+ background: var(--bg-card);
47
+ border-radius: 8px;
48
+ padding: 16px;
49
+ }
50
+ .card-label {
51
+ font-size: 11px;
52
+ color: var(--text-muted);
53
+ text-transform: uppercase;
54
+ letter-spacing: 1px;
55
+ }
56
+ .card-value {
57
+ font-size: 24px;
58
+ font-weight: 700;
59
+ margin-top: 4px;
60
+ }
61
+ .card-sub {
62
+ font-size: 11px;
63
+ color: var(--text-secondary);
64
+ margin-top: 2px;
65
+ }
66
+ #val-api-cost { color: var(--amber); }
67
+ #val-savings { color: var(--green); }
68
+ #val-cache-rate { color: var(--blue-light); }
69
+
70
+ .chart-section {
71
+ background: var(--bg-card);
72
+ border-radius: 8px;
73
+ padding: 20px;
74
+ margin-bottom: 12px;
75
+ }
76
+ .chart-section h2 {
77
+ font-size: 14px;
78
+ font-weight: 600;
79
+ margin-bottom: 16px;
80
+ }
81
+ .chart-header {
82
+ display: flex;
83
+ justify-content: space-between;
84
+ align-items: center;
85
+ margin-bottom: 16px;
86
+ }
87
+ .chart-header h2 { margin-bottom: 0; }
88
+ .chart-container { min-height: 200px; }
89
+
90
+ .chart-row-3 {
91
+ display: grid;
92
+ grid-template-columns: repeat(3, 1fr);
93
+ gap: 12px;
94
+ margin-bottom: 12px;
95
+ }
96
+
97
+ .granularity-toggle { display: flex; gap: 4px; }
98
+ .granularity-toggle button {
99
+ padding: 4px 12px;
100
+ background: var(--bg-input);
101
+ border: none;
102
+ border-radius: 4px;
103
+ color: var(--text-secondary);
104
+ font-size: 12px;
105
+ cursor: pointer;
106
+ }
107
+ .granularity-toggle button.active {
108
+ background: var(--blue);
109
+ color: white;
110
+ box-shadow: 0 0 0 1px rgba(59,130,246,0.5);
111
+ }
112
+ .granularity-toggle button:disabled {
113
+ opacity: 0.35;
114
+ cursor: not-allowed;
115
+ }
116
+
117
+ .table-controls { display: flex; gap: 8px; }
118
+ .filter-input, .sort-select {
119
+ padding: 6px 10px;
120
+ background: var(--bg-input);
121
+ border: 1px solid var(--border);
122
+ border-radius: 6px;
123
+ color: var(--text-primary);
124
+ font-size: 12px;
125
+ }
126
+ .filter-input { width: 180px; }
127
+
128
+ .table-container { overflow-x: auto; }
129
+ .table-container table {
130
+ width: 100%;
131
+ border-collapse: collapse;
132
+ font-size: 12px;
133
+ }
134
+ .table-container th {
135
+ text-align: left;
136
+ padding: 10px 8px;
137
+ border-bottom: 2px solid var(--bg-input);
138
+ color: var(--text-muted);
139
+ text-transform: uppercase;
140
+ font-size: 10px;
141
+ letter-spacing: 1px;
142
+ cursor: pointer;
143
+ }
144
+ .table-container th.align-right,
145
+ .table-container td.align-right { text-align: right; }
146
+ .table-container td {
147
+ padding: 10px 8px;
148
+ border-bottom: 1px solid var(--bg-primary);
149
+ color: var(--text-secondary);
150
+ }
151
+ .table-container tfoot td {
152
+ border-top: 2px solid var(--bg-input);
153
+ font-weight: 600;
154
+ color: var(--text-primary);
155
+ }
156
+
157
+ .tag {
158
+ display: inline-block;
159
+ padding: 2px 8px;
160
+ border-radius: 4px;
161
+ font-size: 11px;
162
+ }
163
+ .tag-project { background: #1e3a5f; color: var(--blue-light); }
164
+ .tag-model-sonnet { background: #1e3a5f; color: var(--blue-light); }
165
+ .tag-model-opus { background: #3b1764; color: #c084fc; }
166
+ .tag-model-haiku { background: #1a2e1a; color: var(--green); }
167
+
168
+ .pagination {
169
+ display: flex;
170
+ justify-content: center;
171
+ gap: 4px;
172
+ margin-top: 12px;
173
+ }
174
+ .pagination button {
175
+ padding: 4px 10px;
176
+ background: var(--bg-input);
177
+ border: none;
178
+ border-radius: 4px;
179
+ font-size: 11px;
180
+ color: var(--text-secondary);
181
+ cursor: pointer;
182
+ }
183
+ .pagination button.active {
184
+ background: var(--blue);
185
+ color: white;
186
+ }
187
+
188
+ .date-picker {
189
+ display: flex;
190
+ align-items: center;
191
+ gap: 8px;
192
+ }
193
+ .date-picker input {
194
+ padding: 6px 10px;
195
+ background: var(--bg-input);
196
+ border: 1px solid var(--border);
197
+ border-radius: 6px;
198
+ color: var(--text-primary);
199
+ font-size: 12px;
200
+ }
201
+ .date-picker span { color: var(--text-secondary); font-size: 12px; }
202
+
203
+ .refresh-controls {
204
+ display: flex;
205
+ align-items: center;
206
+ gap: 8px;
207
+ }
208
+ .btn-refresh {
209
+ padding: 4px 10px;
210
+ background: var(--bg-input);
211
+ border: 1px solid var(--border);
212
+ border-radius: 6px;
213
+ color: var(--text-primary);
214
+ font-size: 16px;
215
+ cursor: pointer;
216
+ line-height: 1;
217
+ }
218
+ .btn-refresh:hover { background: var(--blue); }
219
+ .auto-refresh-label {
220
+ display: flex;
221
+ align-items: center;
222
+ gap: 4px;
223
+ font-size: 12px;
224
+ color: var(--text-secondary);
225
+ cursor: pointer;
226
+ }
227
+ .last-updated {
228
+ font-size: 11px;
229
+ color: var(--text-muted);
230
+ white-space: nowrap;
231
+ }
232
+
233
+ .plan-selector select, .plan-selector input {
234
+ padding: 6px 10px;
235
+ background: var(--bg-input);
236
+ border: 1px solid var(--border);
237
+ border-radius: 6px;
238
+ color: var(--text-primary);
239
+ font-size: 12px;
240
+ }
241
+
242
+ .d3-tooltip {
243
+ position: absolute;
244
+ padding: 8px 12px;
245
+ background: rgba(15, 23, 42, 0.95);
246
+ border: 1px solid var(--border);
247
+ border-radius: 6px;
248
+ font-size: 12px;
249
+ color: var(--text-primary);
250
+ pointer-events: none;
251
+ z-index: 100;
252
+ }
253
+
254
+ .quota-unavailable {
255
+ color: var(--text-muted);
256
+ font-size: 13px;
257
+ padding: 20px;
258
+ text-align: center;
259
+ }
260
+ #quota-section .chart-container { min-height: auto; }
261
+
262
+ @media (max-width: 768px) {
263
+ .summary-cards { grid-template-columns: repeat(2, 1fr); }
264
+ .chart-row-3 { grid-template-columns: 1fr; }
265
+ }