tlc-claude-code 1.4.9 → 1.5.2

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.
Files changed (122) hide show
  1. package/CLAUDE.md +23 -0
  2. package/CODING-STANDARDS.md +408 -0
  3. package/bin/install.js +2 -0
  4. package/dashboard/dist/components/QualityGatePane.d.ts +38 -0
  5. package/dashboard/dist/components/QualityGatePane.js +31 -0
  6. package/dashboard/dist/components/QualityGatePane.test.d.ts +1 -0
  7. package/dashboard/dist/components/QualityGatePane.test.js +147 -0
  8. package/dashboard/dist/components/orchestration/AgentCard.d.ts +26 -0
  9. package/dashboard/dist/components/orchestration/AgentCard.js +60 -0
  10. package/dashboard/dist/components/orchestration/AgentCard.test.d.ts +1 -0
  11. package/dashboard/dist/components/orchestration/AgentCard.test.js +63 -0
  12. package/dashboard/dist/components/orchestration/AgentControls.d.ts +11 -0
  13. package/dashboard/dist/components/orchestration/AgentControls.js +20 -0
  14. package/dashboard/dist/components/orchestration/AgentControls.test.d.ts +1 -0
  15. package/dashboard/dist/components/orchestration/AgentControls.test.js +52 -0
  16. package/dashboard/dist/components/orchestration/AgentDetail.d.ts +35 -0
  17. package/dashboard/dist/components/orchestration/AgentDetail.js +37 -0
  18. package/dashboard/dist/components/orchestration/AgentDetail.test.d.ts +1 -0
  19. package/dashboard/dist/components/orchestration/AgentDetail.test.js +79 -0
  20. package/dashboard/dist/components/orchestration/AgentList.d.ts +31 -0
  21. package/dashboard/dist/components/orchestration/AgentList.js +47 -0
  22. package/dashboard/dist/components/orchestration/AgentList.test.d.ts +1 -0
  23. package/dashboard/dist/components/orchestration/AgentList.test.js +64 -0
  24. package/dashboard/dist/components/orchestration/CostMeter.d.ts +11 -0
  25. package/dashboard/dist/components/orchestration/CostMeter.js +28 -0
  26. package/dashboard/dist/components/orchestration/CostMeter.test.d.ts +1 -0
  27. package/dashboard/dist/components/orchestration/CostMeter.test.js +50 -0
  28. package/dashboard/dist/components/orchestration/ModelSelector.d.ts +20 -0
  29. package/dashboard/dist/components/orchestration/ModelSelector.js +12 -0
  30. package/dashboard/dist/components/orchestration/ModelSelector.test.d.ts +1 -0
  31. package/dashboard/dist/components/orchestration/ModelSelector.test.js +56 -0
  32. package/dashboard/dist/components/orchestration/OrchestrationDashboard.d.ts +28 -0
  33. package/dashboard/dist/components/orchestration/OrchestrationDashboard.js +28 -0
  34. package/dashboard/dist/components/orchestration/OrchestrationDashboard.test.d.ts +1 -0
  35. package/dashboard/dist/components/orchestration/OrchestrationDashboard.test.js +56 -0
  36. package/dashboard/dist/components/orchestration/QualityIndicator.d.ts +11 -0
  37. package/dashboard/dist/components/orchestration/QualityIndicator.js +37 -0
  38. package/dashboard/dist/components/orchestration/QualityIndicator.test.d.ts +1 -0
  39. package/dashboard/dist/components/orchestration/QualityIndicator.test.js +52 -0
  40. package/dashboard/dist/components/orchestration/index.d.ts +8 -0
  41. package/dashboard/dist/components/orchestration/index.js +8 -0
  42. package/package.json +1 -1
  43. package/server/lib/access-control.js +352 -0
  44. package/server/lib/access-control.test.js +322 -0
  45. package/server/lib/agents-cancel-command.js +139 -0
  46. package/server/lib/agents-cancel-command.test.js +180 -0
  47. package/server/lib/agents-get-command.js +159 -0
  48. package/server/lib/agents-get-command.test.js +167 -0
  49. package/server/lib/agents-list-command.js +150 -0
  50. package/server/lib/agents-list-command.test.js +149 -0
  51. package/server/lib/agents-logs-command.js +126 -0
  52. package/server/lib/agents-logs-command.test.js +198 -0
  53. package/server/lib/agents-retry-command.js +117 -0
  54. package/server/lib/agents-retry-command.test.js +192 -0
  55. package/server/lib/budget-limits.js +222 -0
  56. package/server/lib/budget-limits.test.js +214 -0
  57. package/server/lib/code-generator.js +291 -0
  58. package/server/lib/code-generator.test.js +307 -0
  59. package/server/lib/cost-command.js +290 -0
  60. package/server/lib/cost-command.test.js +202 -0
  61. package/server/lib/cost-optimizer.js +404 -0
  62. package/server/lib/cost-optimizer.test.js +232 -0
  63. package/server/lib/cost-projections.js +302 -0
  64. package/server/lib/cost-projections.test.js +217 -0
  65. package/server/lib/cost-reports.js +277 -0
  66. package/server/lib/cost-reports.test.js +254 -0
  67. package/server/lib/cost-tracker.js +216 -0
  68. package/server/lib/cost-tracker.test.js +302 -0
  69. package/server/lib/crypto-patterns.js +433 -0
  70. package/server/lib/crypto-patterns.test.js +346 -0
  71. package/server/lib/design-command.js +385 -0
  72. package/server/lib/design-command.test.js +249 -0
  73. package/server/lib/design-parser.js +237 -0
  74. package/server/lib/design-parser.test.js +290 -0
  75. package/server/lib/gemini-vision.js +377 -0
  76. package/server/lib/gemini-vision.test.js +282 -0
  77. package/server/lib/input-validator.js +360 -0
  78. package/server/lib/input-validator.test.js +295 -0
  79. package/server/lib/litellm-client.js +232 -0
  80. package/server/lib/litellm-client.test.js +267 -0
  81. package/server/lib/litellm-command.js +291 -0
  82. package/server/lib/litellm-command.test.js +260 -0
  83. package/server/lib/litellm-config.js +273 -0
  84. package/server/lib/litellm-config.test.js +212 -0
  85. package/server/lib/model-pricing.js +189 -0
  86. package/server/lib/model-pricing.test.js +178 -0
  87. package/server/lib/models-command.js +223 -0
  88. package/server/lib/models-command.test.js +193 -0
  89. package/server/lib/optimize-command.js +197 -0
  90. package/server/lib/optimize-command.test.js +193 -0
  91. package/server/lib/orchestration-integration.js +206 -0
  92. package/server/lib/orchestration-integration.test.js +235 -0
  93. package/server/lib/output-encoder.js +308 -0
  94. package/server/lib/output-encoder.test.js +312 -0
  95. package/server/lib/quality-evaluator.js +396 -0
  96. package/server/lib/quality-evaluator.test.js +337 -0
  97. package/server/lib/quality-gate-command.js +340 -0
  98. package/server/lib/quality-gate-command.test.js +321 -0
  99. package/server/lib/quality-gate-scorer.js +378 -0
  100. package/server/lib/quality-gate-scorer.test.js +376 -0
  101. package/server/lib/quality-history.js +265 -0
  102. package/server/lib/quality-history.test.js +359 -0
  103. package/server/lib/quality-presets.js +288 -0
  104. package/server/lib/quality-presets.test.js +269 -0
  105. package/server/lib/quality-retry.js +323 -0
  106. package/server/lib/quality-retry.test.js +325 -0
  107. package/server/lib/quality-thresholds.js +255 -0
  108. package/server/lib/quality-thresholds.test.js +237 -0
  109. package/server/lib/secure-auth.js +333 -0
  110. package/server/lib/secure-auth.test.js +288 -0
  111. package/server/lib/secure-code-command.js +540 -0
  112. package/server/lib/secure-code-command.test.js +309 -0
  113. package/server/lib/secure-errors.js +521 -0
  114. package/server/lib/secure-errors.test.js +298 -0
  115. package/server/lib/vision-command.js +372 -0
  116. package/server/lib/vision-command.test.js +255 -0
  117. package/server/lib/visual-command.js +350 -0
  118. package/server/lib/visual-command.test.js +256 -0
  119. package/server/lib/visual-testing.js +315 -0
  120. package/server/lib/visual-testing.test.js +357 -0
  121. package/server/package-lock.json +2 -2
  122. package/server/package.json +1 -1
@@ -0,0 +1,254 @@
1
+ /**
2
+ * Cost Reports Tests
3
+ *
4
+ * Generate cost reports by various dimensions
5
+ */
6
+
7
+ const { describe, it, beforeEach } = require('node:test');
8
+ const assert = require('node:assert');
9
+
10
+ const {
11
+ generateReport,
12
+ filterByPeriod,
13
+ groupByModel,
14
+ groupByOperation,
15
+ groupByTrigger,
16
+ exportCSV,
17
+ exportJSON,
18
+ analyzeTrends,
19
+ compareToLastPeriod,
20
+ formatReport,
21
+ } = require('./cost-reports.js');
22
+
23
+ describe('Cost Reports', () => {
24
+ const sampleData = [
25
+ { date: '2025-01-15', model: 'claude-3-opus', operation: 'code-gen', trigger: 'manual', cost: 1.00 },
26
+ { date: '2025-01-15', model: 'gpt-4', operation: 'review', trigger: 'ci', cost: 0.50 },
27
+ { date: '2025-01-16', model: 'claude-3-opus', operation: 'code-gen', trigger: 'manual', cost: 1.50 },
28
+ { date: '2025-01-16', model: 'claude-3-haiku', operation: 'chat', trigger: 'webhook', cost: 0.10 },
29
+ { date: '2025-01-17', model: 'gpt-4', operation: 'review', trigger: 'ci', cost: 0.75 },
30
+ ];
31
+
32
+ describe('generateReport', () => {
33
+ it('creates report object', () => {
34
+ const report = generateReport(sampleData);
35
+
36
+ assert.ok(report);
37
+ assert.ok(report.totalCost !== undefined);
38
+ assert.ok(report.recordCount !== undefined);
39
+ assert.ok(report.dateRange);
40
+ });
41
+
42
+ it('calculates total cost', () => {
43
+ const report = generateReport(sampleData);
44
+
45
+ assert.strictEqual(report.totalCost, 3.85);
46
+ });
47
+ });
48
+
49
+ describe('filterByPeriod', () => {
50
+ it('selects date range', () => {
51
+ const filtered = filterByPeriod(sampleData, {
52
+ startDate: '2025-01-15',
53
+ endDate: '2025-01-16',
54
+ });
55
+
56
+ assert.strictEqual(filtered.length, 4);
57
+ });
58
+
59
+ it('handles single day', () => {
60
+ const filtered = filterByPeriod(sampleData, {
61
+ startDate: '2025-01-15',
62
+ endDate: '2025-01-15',
63
+ });
64
+
65
+ assert.strictEqual(filtered.length, 2);
66
+ });
67
+
68
+ it('returns empty for no matches', () => {
69
+ const filtered = filterByPeriod(sampleData, {
70
+ startDate: '2024-01-01',
71
+ endDate: '2024-01-31',
72
+ });
73
+
74
+ assert.strictEqual(filtered.length, 0);
75
+ });
76
+ });
77
+
78
+ describe('groupByModel', () => {
79
+ it('aggregates costs by model', () => {
80
+ const grouped = groupByModel(sampleData);
81
+
82
+ assert.ok(grouped['claude-3-opus']);
83
+ assert.ok(grouped['gpt-4']);
84
+ assert.ok(grouped['claude-3-haiku']);
85
+
86
+ assert.strictEqual(grouped['claude-3-opus'], 2.50);
87
+ assert.strictEqual(grouped['gpt-4'], 1.25);
88
+ assert.strictEqual(grouped['claude-3-haiku'], 0.10);
89
+ });
90
+ });
91
+
92
+ describe('groupByOperation', () => {
93
+ it('aggregates costs by operation', () => {
94
+ const grouped = groupByOperation(sampleData);
95
+
96
+ assert.ok(grouped['code-gen']);
97
+ assert.ok(grouped['review']);
98
+ assert.ok(grouped['chat']);
99
+
100
+ assert.strictEqual(grouped['code-gen'], 2.50);
101
+ assert.strictEqual(grouped['review'], 1.25);
102
+ assert.strictEqual(grouped['chat'], 0.10);
103
+ });
104
+ });
105
+
106
+ describe('groupByTrigger', () => {
107
+ it('aggregates costs by trigger', () => {
108
+ const grouped = groupByTrigger(sampleData);
109
+
110
+ assert.ok(grouped['manual']);
111
+ assert.ok(grouped['ci']);
112
+ assert.ok(grouped['webhook']);
113
+
114
+ assert.strictEqual(grouped['manual'], 2.50);
115
+ assert.strictEqual(grouped['ci'], 1.25);
116
+ assert.strictEqual(grouped['webhook'], 0.10);
117
+ });
118
+ });
119
+
120
+ describe('exportCSV', () => {
121
+ it('formats correctly', () => {
122
+ const csv = exportCSV(sampleData);
123
+
124
+ assert.ok(typeof csv === 'string');
125
+ assert.ok(csv.includes('date'));
126
+ assert.ok(csv.includes('model'));
127
+ assert.ok(csv.includes('cost'));
128
+ assert.ok(csv.includes('claude-3-opus'));
129
+
130
+ // Should have header + 5 data rows
131
+ const lines = csv.trim().split('\n');
132
+ assert.strictEqual(lines.length, 6);
133
+ });
134
+ });
135
+
136
+ describe('exportJSON', () => {
137
+ it('formats correctly', () => {
138
+ const json = exportJSON(sampleData);
139
+
140
+ assert.ok(typeof json === 'string');
141
+
142
+ const parsed = JSON.parse(json);
143
+ assert.ok(Array.isArray(parsed));
144
+ assert.strictEqual(parsed.length, 5);
145
+ });
146
+
147
+ it('includes all fields', () => {
148
+ const json = exportJSON(sampleData);
149
+ const parsed = JSON.parse(json);
150
+
151
+ assert.ok(parsed[0].date);
152
+ assert.ok(parsed[0].model);
153
+ assert.ok(parsed[0].operation);
154
+ assert.ok(parsed[0].trigger);
155
+ assert.ok(parsed[0].cost !== undefined);
156
+ });
157
+ });
158
+
159
+ describe('analyzeTrends', () => {
160
+ it('shows direction increasing', () => {
161
+ const data = [
162
+ { date: '2025-01-01', cost: 1.00 },
163
+ { date: '2025-01-02', cost: 1.50 },
164
+ { date: '2025-01-03', cost: 2.00 },
165
+ { date: '2025-01-04', cost: 2.50 },
166
+ ];
167
+
168
+ const trend = analyzeTrends(data);
169
+
170
+ assert.strictEqual(trend.direction, 'increasing');
171
+ assert.ok(trend.percentChange > 0);
172
+ });
173
+
174
+ it('shows direction decreasing', () => {
175
+ const data = [
176
+ { date: '2025-01-01', cost: 2.50 },
177
+ { date: '2025-01-02', cost: 2.00 },
178
+ { date: '2025-01-03', cost: 1.50 },
179
+ { date: '2025-01-04', cost: 1.00 },
180
+ ];
181
+
182
+ const trend = analyzeTrends(data);
183
+
184
+ assert.strictEqual(trend.direction, 'decreasing');
185
+ assert.ok(trend.percentChange < 0);
186
+ });
187
+
188
+ it('shows direction stable', () => {
189
+ const data = [
190
+ { date: '2025-01-01', cost: 1.00 },
191
+ { date: '2025-01-02', cost: 1.00 },
192
+ { date: '2025-01-03', cost: 1.00 },
193
+ ];
194
+
195
+ const trend = analyzeTrends(data);
196
+
197
+ assert.strictEqual(trend.direction, 'stable');
198
+ });
199
+ });
200
+
201
+ describe('compareToLastPeriod', () => {
202
+ it('calculates difference', () => {
203
+ const currentPeriod = [
204
+ { cost: 5.00 },
205
+ { cost: 3.00 },
206
+ ];
207
+
208
+ const lastPeriod = [
209
+ { cost: 4.00 },
210
+ { cost: 2.00 },
211
+ ];
212
+
213
+ const comparison = compareToLastPeriod(currentPeriod, lastPeriod);
214
+
215
+ assert.strictEqual(comparison.currentTotal, 8.00);
216
+ assert.strictEqual(comparison.lastTotal, 6.00);
217
+ assert.strictEqual(comparison.difference, 2.00);
218
+ assert.ok(comparison.percentChange > 0);
219
+ });
220
+
221
+ it('handles empty last period', () => {
222
+ const currentPeriod = [{ cost: 5.00 }];
223
+ const lastPeriod = [];
224
+
225
+ const comparison = compareToLastPeriod(currentPeriod, lastPeriod);
226
+
227
+ assert.strictEqual(comparison.currentTotal, 5.00);
228
+ assert.strictEqual(comparison.lastTotal, 0);
229
+ });
230
+ });
231
+
232
+ describe('formatReport', () => {
233
+ it('creates markdown report', () => {
234
+ const report = generateReport(sampleData);
235
+ const formatted = formatReport(report);
236
+
237
+ assert.ok(typeof formatted === 'string');
238
+ assert.ok(formatted.includes('#')); // Markdown headers
239
+ assert.ok(formatted.includes('Total'));
240
+ assert.ok(formatted.includes('$'));
241
+ });
242
+
243
+ it('includes breakdown sections', () => {
244
+ const report = generateReport(sampleData);
245
+ report.byModel = groupByModel(sampleData);
246
+ report.byOperation = groupByOperation(sampleData);
247
+
248
+ const formatted = formatReport(report);
249
+
250
+ assert.ok(formatted.includes('Model'));
251
+ assert.ok(formatted.includes('Operation'));
252
+ });
253
+ });
254
+ });
@@ -0,0 +1,216 @@
1
+ /**
2
+ * Cost Tracker Module
3
+ *
4
+ * Real-time cost tracking per agent, session, and day
5
+ */
6
+
7
+ const fs = require('fs');
8
+ const path = require('path');
9
+
10
+ /**
11
+ * Create a new cost tracker instance
12
+ * @param {Object} options - Configuration options
13
+ * @param {Object} options.fs - File system module (for testing)
14
+ * @returns {Object} Cost tracker instance
15
+ */
16
+ function createCostTracker(options = {}) {
17
+ const fsModule = options.fs || fs;
18
+
19
+ return {
20
+ records: [],
21
+ byAgent: {},
22
+ bySession: {},
23
+ byDay: {},
24
+ byModel: {},
25
+ byProvider: {},
26
+ fs: fsModule,
27
+ };
28
+ }
29
+
30
+ /**
31
+ * Record a cost entry
32
+ * @param {Object} tracker - Cost tracker instance
33
+ * @param {Object} entry - Cost entry
34
+ * @param {string} entry.agentId - Agent identifier
35
+ * @param {string} entry.sessionId - Session identifier
36
+ * @param {string} entry.model - Model name
37
+ * @param {string} entry.provider - Provider name
38
+ * @param {number} entry.cost - Cost in dollars
39
+ * @param {number} [entry.inputTokens] - Input token count
40
+ * @param {number} [entry.outputTokens] - Output token count
41
+ * @param {string} [entry.timestamp] - ISO timestamp
42
+ */
43
+ function recordCost(tracker, entry) {
44
+ const timestamp = entry.timestamp || new Date().toISOString();
45
+ const day = timestamp.split('T')[0];
46
+
47
+ const record = {
48
+ ...entry,
49
+ timestamp,
50
+ day,
51
+ };
52
+
53
+ tracker.records.push(record);
54
+
55
+ // Aggregate by agent
56
+ tracker.byAgent[entry.agentId] = (tracker.byAgent[entry.agentId] || 0) + entry.cost;
57
+
58
+ // Aggregate by session
59
+ tracker.bySession[entry.sessionId] = (tracker.bySession[entry.sessionId] || 0) + entry.cost;
60
+
61
+ // Aggregate by day
62
+ tracker.byDay[day] = (tracker.byDay[day] || 0) + entry.cost;
63
+
64
+ // Aggregate by model
65
+ tracker.byModel[entry.model] = (tracker.byModel[entry.model] || 0) + entry.cost;
66
+
67
+ // Aggregate by provider
68
+ tracker.byProvider[entry.provider] = (tracker.byProvider[entry.provider] || 0) + entry.cost;
69
+ }
70
+
71
+ /**
72
+ * Get cost for a specific agent
73
+ * @param {Object} tracker - Cost tracker instance
74
+ * @param {string} agentId - Agent identifier
75
+ * @returns {number} Total cost for agent
76
+ */
77
+ function getAgentCost(tracker, agentId) {
78
+ return tracker.byAgent[agentId] || 0;
79
+ }
80
+
81
+ /**
82
+ * Get cost for a specific session
83
+ * @param {Object} tracker - Cost tracker instance
84
+ * @param {string} sessionId - Session identifier
85
+ * @returns {number} Total cost for session
86
+ */
87
+ function getSessionCost(tracker, sessionId) {
88
+ return tracker.bySession[sessionId] || 0;
89
+ }
90
+
91
+ /**
92
+ * Get cost for a specific day
93
+ * @param {Object} tracker - Cost tracker instance
94
+ * @param {string} day - Day in YYYY-MM-DD format
95
+ * @returns {number} Total cost for day
96
+ */
97
+ function getDailyCost(tracker, day) {
98
+ return tracker.byDay[day] || 0;
99
+ }
100
+
101
+ /**
102
+ * Get cost for current week
103
+ * @param {Object} tracker - Cost tracker instance
104
+ * @returns {number} Total cost for current week
105
+ */
106
+ function getWeeklyCost(tracker) {
107
+ const now = new Date();
108
+ const weekStart = new Date(now);
109
+ weekStart.setDate(now.getDate() - now.getDay());
110
+ weekStart.setHours(0, 0, 0, 0);
111
+
112
+ let total = 0;
113
+ for (const [day, cost] of Object.entries(tracker.byDay)) {
114
+ const dayDate = new Date(day);
115
+ if (dayDate >= weekStart && dayDate <= now) {
116
+ total += cost;
117
+ }
118
+ }
119
+
120
+ return total;
121
+ }
122
+
123
+ /**
124
+ * Get cost for current month
125
+ * @param {Object} tracker - Cost tracker instance
126
+ * @returns {number} Total cost for current month
127
+ */
128
+ function getMonthlyCost(tracker) {
129
+ const now = new Date();
130
+ const monthStart = new Date(now.getFullYear(), now.getMonth(), 1);
131
+
132
+ let total = 0;
133
+ for (const [day, cost] of Object.entries(tracker.byDay)) {
134
+ const dayDate = new Date(day);
135
+ if (dayDate >= monthStart && dayDate <= now) {
136
+ total += cost;
137
+ }
138
+ }
139
+
140
+ return total;
141
+ }
142
+
143
+ /**
144
+ * Get costs grouped by model
145
+ * @param {Object} tracker - Cost tracker instance
146
+ * @returns {Object} Costs by model
147
+ */
148
+ function getCostByModel(tracker) {
149
+ return { ...tracker.byModel };
150
+ }
151
+
152
+ /**
153
+ * Get costs grouped by provider
154
+ * @param {Object} tracker - Cost tracker instance
155
+ * @returns {Object} Costs by provider
156
+ */
157
+ function getCostByProvider(tracker) {
158
+ return { ...tracker.byProvider };
159
+ }
160
+
161
+ /**
162
+ * Persist costs to disk
163
+ * @param {Object} tracker - Cost tracker instance
164
+ * @param {string} filePath - Path to save file
165
+ */
166
+ function persistCosts(tracker, filePath) {
167
+ const data = {
168
+ records: tracker.records,
169
+ savedAt: new Date().toISOString(),
170
+ };
171
+
172
+ tracker.fs.writeFileSync(filePath, JSON.stringify(data, null, 2));
173
+ }
174
+
175
+ /**
176
+ * Load costs from disk
177
+ * @param {string} filePath - Path to load from
178
+ * @param {Object} options - Options including fs module
179
+ * @returns {Object} Cost tracker instance with loaded data
180
+ */
181
+ function loadCosts(filePath, options = {}) {
182
+ const fsModule = options.fs || fs;
183
+ const tracker = createCostTracker(options);
184
+
185
+ if (!fsModule.existsSync(filePath)) {
186
+ return tracker;
187
+ }
188
+
189
+ try {
190
+ const data = JSON.parse(fsModule.readFileSync(filePath, 'utf-8'));
191
+
192
+ if (data.records && Array.isArray(data.records)) {
193
+ for (const record of data.records) {
194
+ recordCost(tracker, record);
195
+ }
196
+ }
197
+ } catch (err) {
198
+ // Return empty tracker on error
199
+ }
200
+
201
+ return tracker;
202
+ }
203
+
204
+ module.exports = {
205
+ createCostTracker,
206
+ recordCost,
207
+ getAgentCost,
208
+ getSessionCost,
209
+ getDailyCost,
210
+ getWeeklyCost,
211
+ getMonthlyCost,
212
+ getCostByModel,
213
+ getCostByProvider,
214
+ persistCosts,
215
+ loadCosts,
216
+ };