tlc-claude-code 1.2.29 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dashboard/dist/components/UsagePane.d.ts +13 -0
- package/dashboard/dist/components/UsagePane.js +51 -0
- package/dashboard/dist/components/UsagePane.test.d.ts +1 -0
- package/dashboard/dist/components/UsagePane.test.js +142 -0
- package/dashboard/dist/components/WorkspaceDocsPane.d.ts +19 -0
- package/dashboard/dist/components/WorkspaceDocsPane.js +146 -0
- package/dashboard/dist/components/WorkspaceDocsPane.test.d.ts +1 -0
- package/dashboard/dist/components/WorkspaceDocsPane.test.js +242 -0
- package/dashboard/dist/components/WorkspacePane.d.ts +18 -0
- package/dashboard/dist/components/WorkspacePane.js +17 -0
- package/dashboard/dist/components/WorkspacePane.test.d.ts +1 -0
- package/dashboard/dist/components/WorkspacePane.test.js +84 -0
- package/package.json +1 -1
- package/server/lib/architecture-command.js +450 -0
- package/server/lib/architecture-command.test.js +754 -0
- package/server/lib/ast-analyzer.js +324 -0
- package/server/lib/ast-analyzer.test.js +437 -0
- package/server/lib/auth-system.test.js +4 -1
- package/server/lib/boundary-detector.js +427 -0
- package/server/lib/boundary-detector.test.js +320 -0
- package/server/lib/budget-alerts.js +138 -0
- package/server/lib/budget-alerts.test.js +235 -0
- package/server/lib/candidates-tracker.js +210 -0
- package/server/lib/candidates-tracker.test.js +300 -0
- package/server/lib/checkpoint-manager.js +251 -0
- package/server/lib/checkpoint-manager.test.js +474 -0
- package/server/lib/circular-detector.js +337 -0
- package/server/lib/circular-detector.test.js +353 -0
- package/server/lib/cohesion-analyzer.js +310 -0
- package/server/lib/cohesion-analyzer.test.js +447 -0
- package/server/lib/contract-testing.js +625 -0
- package/server/lib/contract-testing.test.js +342 -0
- package/server/lib/conversion-planner.js +469 -0
- package/server/lib/conversion-planner.test.js +361 -0
- package/server/lib/convert-command.js +351 -0
- package/server/lib/convert-command.test.js +608 -0
- package/server/lib/coupling-calculator.js +189 -0
- package/server/lib/coupling-calculator.test.js +509 -0
- package/server/lib/dependency-graph.js +367 -0
- package/server/lib/dependency-graph.test.js +516 -0
- package/server/lib/duplication-detector.js +349 -0
- package/server/lib/duplication-detector.test.js +401 -0
- package/server/lib/example-service.js +616 -0
- package/server/lib/example-service.test.js +397 -0
- package/server/lib/impact-scorer.js +184 -0
- package/server/lib/impact-scorer.test.js +211 -0
- package/server/lib/mermaid-generator.js +358 -0
- package/server/lib/mermaid-generator.test.js +301 -0
- package/server/lib/messaging-patterns.js +750 -0
- package/server/lib/messaging-patterns.test.js +213 -0
- package/server/lib/microservice-template.js +386 -0
- package/server/lib/microservice-template.test.js +325 -0
- package/server/lib/new-project-microservice.js +450 -0
- package/server/lib/new-project-microservice.test.js +600 -0
- package/server/lib/refactor-command.js +326 -0
- package/server/lib/refactor-command.test.js +528 -0
- package/server/lib/refactor-executor.js +254 -0
- package/server/lib/refactor-executor.test.js +305 -0
- package/server/lib/refactor-observer.js +292 -0
- package/server/lib/refactor-observer.test.js +422 -0
- package/server/lib/refactor-progress.js +193 -0
- package/server/lib/refactor-progress.test.js +251 -0
- package/server/lib/refactor-reporter.js +237 -0
- package/server/lib/refactor-reporter.test.js +247 -0
- package/server/lib/semantic-analyzer.js +198 -0
- package/server/lib/semantic-analyzer.test.js +474 -0
- package/server/lib/service-scaffold.js +486 -0
- package/server/lib/service-scaffold.test.js +373 -0
- package/server/lib/shared-kernel.js +578 -0
- package/server/lib/shared-kernel.test.js +255 -0
- package/server/lib/traefik-config.js +282 -0
- package/server/lib/traefik-config.test.js +312 -0
- package/server/lib/usage-command.js +218 -0
- package/server/lib/usage-command.test.js +391 -0
- package/server/lib/usage-formatter.js +192 -0
- package/server/lib/usage-formatter.test.js +267 -0
- package/server/lib/usage-history.js +122 -0
- package/server/lib/usage-history.test.js +206 -0
- package/server/package-lock.json +14 -0
- package/server/package.json +1 -0
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Traefik Config Generator Tests
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, it, expect } from 'vitest';
|
|
6
|
+
|
|
7
|
+
describe('TraefikConfig', () => {
|
|
8
|
+
describe('generateTraefikYml', () => {
|
|
9
|
+
it('generates valid traefik.yml structure', async () => {
|
|
10
|
+
const { TraefikConfig } = await import('./traefik-config.js');
|
|
11
|
+
const traefik = new TraefikConfig();
|
|
12
|
+
|
|
13
|
+
const config = {
|
|
14
|
+
services: ['user', 'order'],
|
|
15
|
+
domain: 'localhost',
|
|
16
|
+
tls: false,
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const result = traefik.generateTraefikYml(config);
|
|
20
|
+
|
|
21
|
+
expect(result).toContain('entryPoints:');
|
|
22
|
+
expect(result).toContain('api:');
|
|
23
|
+
expect(result).toContain('providers:');
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('entrypoint web on port 80', async () => {
|
|
27
|
+
const { TraefikConfig } = await import('./traefik-config.js');
|
|
28
|
+
const traefik = new TraefikConfig();
|
|
29
|
+
|
|
30
|
+
const config = {
|
|
31
|
+
services: ['user'],
|
|
32
|
+
domain: 'localhost',
|
|
33
|
+
tls: false,
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const result = traefik.generateTraefikYml(config);
|
|
37
|
+
|
|
38
|
+
expect(result).toContain('web:');
|
|
39
|
+
expect(result).toContain(':80');
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('entrypoint websecure when tls enabled', async () => {
|
|
43
|
+
const { TraefikConfig } = await import('./traefik-config.js');
|
|
44
|
+
const traefik = new TraefikConfig();
|
|
45
|
+
|
|
46
|
+
const config = {
|
|
47
|
+
services: ['user'],
|
|
48
|
+
domain: 'example.com',
|
|
49
|
+
tls: true,
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const result = traefik.generateTraefikYml(config);
|
|
53
|
+
|
|
54
|
+
expect(result).toContain('websecure:');
|
|
55
|
+
expect(result).toContain(':443');
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('docker provider configured', async () => {
|
|
59
|
+
const { TraefikConfig } = await import('./traefik-config.js');
|
|
60
|
+
const traefik = new TraefikConfig();
|
|
61
|
+
|
|
62
|
+
const config = {
|
|
63
|
+
services: ['user'],
|
|
64
|
+
domain: 'localhost',
|
|
65
|
+
tls: false,
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const result = traefik.generateTraefikYml(config);
|
|
69
|
+
|
|
70
|
+
expect(result).toContain('docker:');
|
|
71
|
+
expect(result).toContain('exposedByDefault: false');
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('API dashboard enabled', async () => {
|
|
75
|
+
const { TraefikConfig } = await import('./traefik-config.js');
|
|
76
|
+
const traefik = new TraefikConfig();
|
|
77
|
+
|
|
78
|
+
const config = {
|
|
79
|
+
services: ['user'],
|
|
80
|
+
domain: 'localhost',
|
|
81
|
+
tls: false,
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const result = traefik.generateTraefikYml(config);
|
|
85
|
+
|
|
86
|
+
expect(result).toContain('api:');
|
|
87
|
+
expect(result).toContain('dashboard: true');
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
describe('generateDynamicConfig', () => {
|
|
92
|
+
it('routes /api/user to user-service', async () => {
|
|
93
|
+
const { TraefikConfig } = await import('./traefik-config.js');
|
|
94
|
+
const traefik = new TraefikConfig();
|
|
95
|
+
|
|
96
|
+
const config = {
|
|
97
|
+
services: ['user', 'order'],
|
|
98
|
+
domain: 'localhost',
|
|
99
|
+
tls: false,
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const result = traefik.generateDynamicConfig(config);
|
|
103
|
+
|
|
104
|
+
expect(result).toContain('user-router:');
|
|
105
|
+
expect(result).toContain('PathPrefix(`/api/user`)');
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('routes /api/order to order-service', async () => {
|
|
109
|
+
const { TraefikConfig } = await import('./traefik-config.js');
|
|
110
|
+
const traefik = new TraefikConfig();
|
|
111
|
+
|
|
112
|
+
const config = {
|
|
113
|
+
services: ['user', 'order'],
|
|
114
|
+
domain: 'localhost',
|
|
115
|
+
tls: false,
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const result = traefik.generateDynamicConfig(config);
|
|
119
|
+
|
|
120
|
+
expect(result).toContain('order-router:');
|
|
121
|
+
expect(result).toContain('PathPrefix(`/api/order`)');
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('health check middleware included', async () => {
|
|
125
|
+
const { TraefikConfig } = await import('./traefik-config.js');
|
|
126
|
+
const traefik = new TraefikConfig();
|
|
127
|
+
|
|
128
|
+
const config = {
|
|
129
|
+
services: ['user'],
|
|
130
|
+
domain: 'localhost',
|
|
131
|
+
tls: false,
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const result = traefik.generateDynamicConfig(config);
|
|
135
|
+
|
|
136
|
+
expect(result).toContain('middlewares:');
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it('rate limiting middleware configured', async () => {
|
|
140
|
+
const { TraefikConfig } = await import('./traefik-config.js');
|
|
141
|
+
const traefik = new TraefikConfig();
|
|
142
|
+
|
|
143
|
+
const config = {
|
|
144
|
+
services: ['user'],
|
|
145
|
+
domain: 'localhost',
|
|
146
|
+
tls: false,
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
const result = traefik.generateDynamicConfig(config);
|
|
150
|
+
|
|
151
|
+
expect(result).toContain('rate-limit:');
|
|
152
|
+
expect(result).toContain('rateLimit:');
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
describe('generateServiceRouter', () => {
|
|
157
|
+
it('generates router with PathPrefix rule', async () => {
|
|
158
|
+
const { TraefikConfig } = await import('./traefik-config.js');
|
|
159
|
+
const traefik = new TraefikConfig();
|
|
160
|
+
|
|
161
|
+
const result = traefik.generateServiceRouter('user', 3000);
|
|
162
|
+
|
|
163
|
+
expect(result).toContain('user-router:');
|
|
164
|
+
expect(result).toContain('PathPrefix(`/api/user`)');
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it('includes strip prefix middleware', async () => {
|
|
168
|
+
const { TraefikConfig } = await import('./traefik-config.js');
|
|
169
|
+
const traefik = new TraefikConfig();
|
|
170
|
+
|
|
171
|
+
const result = traefik.generateServiceRouter('user', 3000);
|
|
172
|
+
|
|
173
|
+
expect(result).toContain('middlewares:');
|
|
174
|
+
expect(result).toContain('strip-user');
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it('configures service backend with port', async () => {
|
|
178
|
+
const { TraefikConfig } = await import('./traefik-config.js');
|
|
179
|
+
const traefik = new TraefikConfig();
|
|
180
|
+
|
|
181
|
+
const result = traefik.generateServiceRouter('user', 3001);
|
|
182
|
+
|
|
183
|
+
expect(result).toContain('user-service:');
|
|
184
|
+
expect(result).toContain('http://user-service:3001');
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it('handles custom service port', async () => {
|
|
188
|
+
const { TraefikConfig } = await import('./traefik-config.js');
|
|
189
|
+
const traefik = new TraefikConfig();
|
|
190
|
+
|
|
191
|
+
const result = traefik.generateServiceRouter('order', 4000);
|
|
192
|
+
|
|
193
|
+
expect(result).toContain('http://order-service:4000');
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
describe('generateMiddlewares', () => {
|
|
198
|
+
it('rate limit average 100', async () => {
|
|
199
|
+
const { TraefikConfig } = await import('./traefik-config.js');
|
|
200
|
+
const traefik = new TraefikConfig();
|
|
201
|
+
|
|
202
|
+
const result = traefik.generateMiddlewares();
|
|
203
|
+
|
|
204
|
+
expect(result).toContain('rate-limit:');
|
|
205
|
+
expect(result).toContain('average: 100');
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
it('rate limit burst 50', async () => {
|
|
209
|
+
const { TraefikConfig } = await import('./traefik-config.js');
|
|
210
|
+
const traefik = new TraefikConfig();
|
|
211
|
+
|
|
212
|
+
const result = traefik.generateMiddlewares();
|
|
213
|
+
|
|
214
|
+
expect(result).toContain('burst: 50');
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
it('strip prefix middleware', async () => {
|
|
218
|
+
const { TraefikConfig } = await import('./traefik-config.js');
|
|
219
|
+
const traefik = new TraefikConfig();
|
|
220
|
+
|
|
221
|
+
const result = traefik.generateMiddlewares();
|
|
222
|
+
|
|
223
|
+
expect(result).toContain('stripPrefix:');
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
it('security headers middleware', async () => {
|
|
227
|
+
const { TraefikConfig } = await import('./traefik-config.js');
|
|
228
|
+
const traefik = new TraefikConfig();
|
|
229
|
+
|
|
230
|
+
const result = traefik.generateMiddlewares();
|
|
231
|
+
|
|
232
|
+
expect(result).toContain('headers:');
|
|
233
|
+
expect(result).toContain('customResponseHeaders:');
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
describe('generateTlsConfig', () => {
|
|
238
|
+
it('TLS config generated when enabled', async () => {
|
|
239
|
+
const { TraefikConfig } = await import('./traefik-config.js');
|
|
240
|
+
const traefik = new TraefikConfig();
|
|
241
|
+
|
|
242
|
+
const result = traefik.generateTlsConfig('example.com');
|
|
243
|
+
|
|
244
|
+
expect(result).toContain('certificatesResolvers:');
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
it('uses Let\'s Encrypt resolver', async () => {
|
|
248
|
+
const { TraefikConfig } = await import('./traefik-config.js');
|
|
249
|
+
const traefik = new TraefikConfig();
|
|
250
|
+
|
|
251
|
+
const result = traefik.generateTlsConfig('example.com');
|
|
252
|
+
|
|
253
|
+
expect(result).toContain('letsencrypt:');
|
|
254
|
+
expect(result).toContain('acme:');
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
it('includes certificate storage', async () => {
|
|
258
|
+
const { TraefikConfig } = await import('./traefik-config.js');
|
|
259
|
+
const traefik = new TraefikConfig();
|
|
260
|
+
|
|
261
|
+
const result = traefik.generateTlsConfig('example.com');
|
|
262
|
+
|
|
263
|
+
expect(result).toContain('storage:');
|
|
264
|
+
expect(result).toContain('acme.json');
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
it('domain configurable', async () => {
|
|
268
|
+
const { TraefikConfig } = await import('./traefik-config.js');
|
|
269
|
+
const traefik = new TraefikConfig();
|
|
270
|
+
|
|
271
|
+
const result = traefik.generateTlsConfig('myapp.example.com');
|
|
272
|
+
|
|
273
|
+
expect(result).toContain('myapp.example.com');
|
|
274
|
+
});
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
describe('edge cases', () => {
|
|
278
|
+
it('handles single service', async () => {
|
|
279
|
+
const { TraefikConfig } = await import('./traefik-config.js');
|
|
280
|
+
const traefik = new TraefikConfig();
|
|
281
|
+
|
|
282
|
+
const config = {
|
|
283
|
+
services: ['api'],
|
|
284
|
+
domain: 'localhost',
|
|
285
|
+
tls: false,
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
const result = traefik.generateDynamicConfig(config);
|
|
289
|
+
|
|
290
|
+
expect(result).toContain('api-router:');
|
|
291
|
+
expect(result).toContain('PathPrefix(`/api/api`)');
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
it('handles multiple services', async () => {
|
|
295
|
+
const { TraefikConfig } = await import('./traefik-config.js');
|
|
296
|
+
const traefik = new TraefikConfig();
|
|
297
|
+
|
|
298
|
+
const config = {
|
|
299
|
+
services: ['user', 'order', 'notification', 'payment'],
|
|
300
|
+
domain: 'localhost',
|
|
301
|
+
tls: false,
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
const result = traefik.generateDynamicConfig(config);
|
|
305
|
+
|
|
306
|
+
expect(result).toContain('user-router:');
|
|
307
|
+
expect(result).toContain('order-router:');
|
|
308
|
+
expect(result).toContain('notification-router:');
|
|
309
|
+
expect(result).toContain('payment-router:');
|
|
310
|
+
});
|
|
311
|
+
});
|
|
312
|
+
});
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Usage Command - /tlc:usage command implementation
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const { BudgetTracker } = require('./budget-tracker.js');
|
|
6
|
+
const { UsageHistory } = require('./usage-history.js');
|
|
7
|
+
const { formatUsageSummary } = require('./usage-formatter.js');
|
|
8
|
+
const { BudgetAlerts, formatAlertMessage } = require('./budget-alerts.js');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Parse command line arguments
|
|
12
|
+
* @param {string[]} args - Command line arguments
|
|
13
|
+
* @returns {Object} Parsed options
|
|
14
|
+
*/
|
|
15
|
+
function parseArgs(args) {
|
|
16
|
+
const result = {
|
|
17
|
+
reset: false,
|
|
18
|
+
model: null,
|
|
19
|
+
json: false,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
for (let i = 0; i < args.length; i++) {
|
|
23
|
+
const arg = args[i];
|
|
24
|
+
|
|
25
|
+
if (arg === '--reset') {
|
|
26
|
+
result.reset = true;
|
|
27
|
+
} else if (arg === '--json') {
|
|
28
|
+
result.json = true;
|
|
29
|
+
} else if (arg === '--model') {
|
|
30
|
+
result.model = args[i + 1];
|
|
31
|
+
i++;
|
|
32
|
+
} else if (arg.startsWith('--model=')) {
|
|
33
|
+
result.model = arg.split('=')[1];
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return result;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* UsageCommand class - handles /tlc:usage command
|
|
42
|
+
*/
|
|
43
|
+
class UsageCommand {
|
|
44
|
+
constructor(options = {}) {
|
|
45
|
+
this.budgetTracker = options.budgetTracker || new BudgetTracker();
|
|
46
|
+
this.usageHistory = options.usageHistory || new UsageHistory();
|
|
47
|
+
this.budgetAlerts = options.budgetAlerts || new BudgetAlerts();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Execute the usage command
|
|
52
|
+
* @param {string[]} args - Command arguments
|
|
53
|
+
* @param {Object} config - Budget config per model { model: { budgetDaily, budgetMonthly } }
|
|
54
|
+
* @returns {Object} Result { success, output, json?, alerts?, error? }
|
|
55
|
+
*/
|
|
56
|
+
execute(args, config) {
|
|
57
|
+
const options = parseArgs(args);
|
|
58
|
+
|
|
59
|
+
// Determine which models to process
|
|
60
|
+
let models = Object.keys(config);
|
|
61
|
+
|
|
62
|
+
if (options.model) {
|
|
63
|
+
if (!config[options.model]) {
|
|
64
|
+
return {
|
|
65
|
+
success: false,
|
|
66
|
+
error: `Unknown model: ${options.model}. Available: ${models.join(', ')}`,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
models = [options.model];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Handle reset
|
|
73
|
+
if (options.reset) {
|
|
74
|
+
return this.handleReset(models, options);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Collect data
|
|
78
|
+
const usageData = this.getUsageData(models);
|
|
79
|
+
const historyData = this.getHistoryData(models);
|
|
80
|
+
const alerts = this.checkAlerts(models, usageData, config);
|
|
81
|
+
const alertMessages = alerts.map(a => formatAlertMessage(a));
|
|
82
|
+
|
|
83
|
+
// JSON output
|
|
84
|
+
if (options.json) {
|
|
85
|
+
return {
|
|
86
|
+
success: true,
|
|
87
|
+
json: {
|
|
88
|
+
models: usageData,
|
|
89
|
+
history: historyData,
|
|
90
|
+
alerts: alerts,
|
|
91
|
+
totals: this.calculateTotals(usageData, config),
|
|
92
|
+
},
|
|
93
|
+
alerts: alertMessages,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Text output
|
|
98
|
+
const output = formatUsageSummary(usageData, config, historyData);
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
success: true,
|
|
102
|
+
output,
|
|
103
|
+
alerts: alertMessages,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Handle --reset flag
|
|
109
|
+
* @param {string[]} models - Models to reset
|
|
110
|
+
* @param {Object} options - Parsed options
|
|
111
|
+
* @returns {Object} Result
|
|
112
|
+
*/
|
|
113
|
+
handleReset(models, options) {
|
|
114
|
+
for (const model of models) {
|
|
115
|
+
this.budgetTracker.reset(model);
|
|
116
|
+
this.budgetAlerts.resetAlerts(model);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const modelList = models.join(', ');
|
|
120
|
+
return {
|
|
121
|
+
success: true,
|
|
122
|
+
output: `Usage reset for: ${modelList}`,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Collect usage data from all specified models
|
|
128
|
+
* @param {string[]} models - Model names
|
|
129
|
+
* @returns {Object} Usage data per model
|
|
130
|
+
*/
|
|
131
|
+
getUsageData(models) {
|
|
132
|
+
const data = {};
|
|
133
|
+
|
|
134
|
+
for (const model of models) {
|
|
135
|
+
data[model] = this.budgetTracker.getUsage(model);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return data;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Collect history data from all specified models
|
|
143
|
+
* @param {string[]} models - Model names
|
|
144
|
+
* @returns {Object} History data per model
|
|
145
|
+
*/
|
|
146
|
+
getHistoryData(models) {
|
|
147
|
+
const data = {};
|
|
148
|
+
|
|
149
|
+
for (const model of models) {
|
|
150
|
+
data[model] = this.usageHistory.getHistory(model);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return data;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Check alerts for all models
|
|
158
|
+
* @param {string[]} models - Model names
|
|
159
|
+
* @param {Object} usageData - Usage data per model
|
|
160
|
+
* @param {Object} config - Budget config per model
|
|
161
|
+
* @returns {Object[]} Array of fired alerts
|
|
162
|
+
*/
|
|
163
|
+
checkAlerts(models, usageData, config) {
|
|
164
|
+
const allAlerts = [];
|
|
165
|
+
|
|
166
|
+
for (const model of models) {
|
|
167
|
+
const usage = usageData[model];
|
|
168
|
+
const modelConfig = config[model];
|
|
169
|
+
|
|
170
|
+
if (usage && modelConfig) {
|
|
171
|
+
const alerts = this.budgetAlerts.checkThresholds(model, usage, modelConfig);
|
|
172
|
+
allAlerts.push(...alerts);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return allAlerts;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Calculate totals across all models
|
|
181
|
+
* @param {Object} usageData - Usage data per model
|
|
182
|
+
* @param {Object} config - Budget config per model
|
|
183
|
+
* @returns {Object} Totals
|
|
184
|
+
*/
|
|
185
|
+
calculateTotals(usageData, config) {
|
|
186
|
+
let totalDaily = 0;
|
|
187
|
+
let totalMonthly = 0;
|
|
188
|
+
let totalRequests = 0;
|
|
189
|
+
let totalBudgetDaily = 0;
|
|
190
|
+
let totalBudgetMonthly = 0;
|
|
191
|
+
|
|
192
|
+
for (const model of Object.keys(usageData)) {
|
|
193
|
+
const usage = usageData[model];
|
|
194
|
+
const modelConfig = config[model] || {};
|
|
195
|
+
|
|
196
|
+
totalDaily += usage.daily || 0;
|
|
197
|
+
totalMonthly += usage.monthly || 0;
|
|
198
|
+
totalRequests += usage.requests || 0;
|
|
199
|
+
totalBudgetDaily += modelConfig.budgetDaily || 0;
|
|
200
|
+
totalBudgetMonthly += modelConfig.budgetMonthly || 0;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return {
|
|
204
|
+
daily: totalDaily,
|
|
205
|
+
monthly: totalMonthly,
|
|
206
|
+
requests: totalRequests,
|
|
207
|
+
budgetDaily: totalBudgetDaily,
|
|
208
|
+
budgetMonthly: totalBudgetMonthly,
|
|
209
|
+
remainingDaily: Math.max(0, totalBudgetDaily - totalDaily),
|
|
210
|
+
remainingMonthly: Math.max(0, totalBudgetMonthly - totalMonthly),
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
module.exports = {
|
|
216
|
+
UsageCommand,
|
|
217
|
+
parseArgs,
|
|
218
|
+
};
|