tlc-claude-code 1.4.4 → 1.4.6
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/App.js +28 -2
- package/dashboard/dist/api/health-diagnostics.d.ts +26 -0
- package/dashboard/dist/api/health-diagnostics.js +85 -0
- package/dashboard/dist/api/health-diagnostics.test.d.ts +1 -0
- package/dashboard/dist/api/health-diagnostics.test.js +126 -0
- package/dashboard/dist/api/index.d.ts +5 -0
- package/dashboard/dist/api/index.js +5 -0
- package/dashboard/dist/api/notes-api.d.ts +18 -0
- package/dashboard/dist/api/notes-api.js +68 -0
- package/dashboard/dist/api/notes-api.test.d.ts +1 -0
- package/dashboard/dist/api/notes-api.test.js +113 -0
- package/dashboard/dist/api/safeFetch.d.ts +50 -0
- package/dashboard/dist/api/safeFetch.js +135 -0
- package/dashboard/dist/api/safeFetch.test.d.ts +1 -0
- package/dashboard/dist/api/safeFetch.test.js +215 -0
- package/dashboard/dist/api/tasks-api.d.ts +32 -0
- package/dashboard/dist/api/tasks-api.js +98 -0
- package/dashboard/dist/api/tasks-api.test.d.ts +1 -0
- package/dashboard/dist/api/tasks-api.test.js +383 -0
- package/dashboard/dist/components/BugsPane.d.ts +20 -0
- package/dashboard/dist/components/BugsPane.js +210 -0
- package/dashboard/dist/components/BugsPane.test.d.ts +1 -0
- package/dashboard/dist/components/BugsPane.test.js +256 -0
- package/dashboard/dist/components/HealthPane.d.ts +3 -1
- package/dashboard/dist/components/HealthPane.js +44 -6
- package/dashboard/dist/components/HealthPane.test.js +105 -2
- package/dashboard/dist/components/RouterPane.d.ts +4 -3
- package/dashboard/dist/components/RouterPane.js +60 -57
- package/dashboard/dist/components/RouterPane.test.js +150 -96
- package/dashboard/dist/components/UpdateBanner.d.ts +26 -0
- package/dashboard/dist/components/UpdateBanner.js +30 -0
- package/dashboard/dist/components/UpdateBanner.test.d.ts +1 -0
- package/dashboard/dist/components/UpdateBanner.test.js +96 -0
- package/dashboard/dist/components/ui/EmptyState.d.ts +14 -0
- package/dashboard/dist/components/ui/EmptyState.js +58 -0
- package/dashboard/dist/components/ui/EmptyState.test.d.ts +1 -0
- package/dashboard/dist/components/ui/EmptyState.test.js +97 -0
- package/dashboard/dist/components/ui/ErrorState.d.ts +17 -0
- package/dashboard/dist/components/ui/ErrorState.js +80 -0
- package/dashboard/dist/components/ui/ErrorState.test.d.ts +1 -0
- package/dashboard/dist/components/ui/ErrorState.test.js +166 -0
- package/dashboard/package.json +3 -0
- package/package.json +4 -1
- package/server/dashboard/index.html +284 -13
- package/server/dashboard/login.html +262 -0
- package/server/index.js +304 -0
- package/server/lib/api-provider.js +104 -186
- package/server/lib/api-provider.test.js +238 -336
- package/server/lib/cli-detector.js +90 -166
- package/server/lib/cli-detector.test.js +114 -269
- package/server/lib/cli-provider.js +142 -212
- package/server/lib/cli-provider.test.js +196 -349
- package/server/lib/debug.test.js +3 -3
- package/server/lib/devserver-router-api.js +54 -249
- package/server/lib/devserver-router-api.test.js +126 -426
- package/server/lib/introspect.js +309 -0
- package/server/lib/introspect.test.js +286 -0
- package/server/lib/model-router.js +107 -245
- package/server/lib/model-router.test.js +122 -313
- package/server/lib/output-schemas.js +146 -269
- package/server/lib/output-schemas.test.js +106 -307
- package/server/lib/plan-parser.js +59 -16
- package/server/lib/provider-interface.js +99 -153
- package/server/lib/provider-interface.test.js +228 -394
- package/server/lib/provider-queue.js +164 -158
- package/server/lib/provider-queue.test.js +186 -315
- package/server/lib/router-config.js +99 -221
- package/server/lib/router-config.test.js +83 -237
- package/server/lib/router-setup-command.js +94 -419
- package/server/lib/router-setup-command.test.js +96 -375
- package/server/lib/router-status-api.js +93 -0
- package/server/lib/router-status-api.test.js +270 -0
|
@@ -1,419 +1,94 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Router Setup Command - Interactive setup for multi-model routing
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
* @returns {Promise<Object>} Detected CLI info
|
|
96
|
-
*/
|
|
97
|
-
export async function detectLocalCLIs() {
|
|
98
|
-
const detected = await detectAllCLIs();
|
|
99
|
-
const result = {};
|
|
100
|
-
|
|
101
|
-
for (const [name, info] of detected) {
|
|
102
|
-
result[name] = {
|
|
103
|
-
detected: true,
|
|
104
|
-
version: info.version,
|
|
105
|
-
capabilities: info.capabilities || [],
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
return result;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Test devserver connection
|
|
114
|
-
* @param {string|null} url - Devserver URL
|
|
115
|
-
* @returns {Promise<Object>} Connection result
|
|
116
|
-
*/
|
|
117
|
-
export async function testDevserverConnection(url) {
|
|
118
|
-
if (!url) {
|
|
119
|
-
return { connected: false, configured: false };
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
try {
|
|
123
|
-
const response = await fetch(`${url}/api/health`);
|
|
124
|
-
if (response.ok) {
|
|
125
|
-
const data = await response.json();
|
|
126
|
-
return {
|
|
127
|
-
connected: true,
|
|
128
|
-
configured: true,
|
|
129
|
-
healthy: data.healthy,
|
|
130
|
-
providers: data.providers,
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
return { connected: false, configured: true, error: 'Unhealthy response' };
|
|
134
|
-
} catch (err) {
|
|
135
|
-
return { connected: false, configured: true, error: err.message };
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Configure a provider
|
|
141
|
-
* @param {Object} config - Current config
|
|
142
|
-
* @param {string} name - Provider name
|
|
143
|
-
* @param {Object} providerConfig - Provider configuration
|
|
144
|
-
* @returns {Object} Updated config
|
|
145
|
-
*/
|
|
146
|
-
export function configureProvider(config, name, providerConfig) {
|
|
147
|
-
return {
|
|
148
|
-
...config,
|
|
149
|
-
providers: {
|
|
150
|
-
...config.providers,
|
|
151
|
-
[name]: providerConfig,
|
|
152
|
-
},
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* Configure a capability
|
|
158
|
-
* @param {Object} config - Current config
|
|
159
|
-
* @param {string} name - Capability name
|
|
160
|
-
* @param {string[]} providers - Provider names
|
|
161
|
-
* @returns {Object} Updated config
|
|
162
|
-
*/
|
|
163
|
-
export function configureCapability(config, name, providers) {
|
|
164
|
-
return {
|
|
165
|
-
...config,
|
|
166
|
-
capabilities: {
|
|
167
|
-
...config.capabilities,
|
|
168
|
-
[name]: { providers },
|
|
169
|
-
},
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* Test provider connectivity
|
|
175
|
-
* @param {Object} provider - Provider config
|
|
176
|
-
* @returns {Promise<Object>} Test result
|
|
177
|
-
*/
|
|
178
|
-
export async function testProvider(provider) {
|
|
179
|
-
if (provider.type === 'cli') {
|
|
180
|
-
// CLI providers are available if detected
|
|
181
|
-
return {
|
|
182
|
-
available: provider.detected === true,
|
|
183
|
-
via: provider.detected ? 'local' : 'devserver',
|
|
184
|
-
};
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
if (provider.type === 'api') {
|
|
188
|
-
// Test API endpoint
|
|
189
|
-
try {
|
|
190
|
-
const response = await fetch(`${provider.baseUrl}/v1/models`);
|
|
191
|
-
return { available: response.ok, via: 'api' };
|
|
192
|
-
} catch (err) {
|
|
193
|
-
return { available: true, via: 'api', note: 'Endpoint not tested' };
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
return { available: false, error: 'Unknown provider type' };
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
/**
|
|
201
|
-
* Format routing summary
|
|
202
|
-
* @param {Object} config - Router config
|
|
203
|
-
* @returns {string} Formatted summary
|
|
204
|
-
*/
|
|
205
|
-
export function formatRoutingSummary(config) {
|
|
206
|
-
const lines = ['Routing Summary:', ''];
|
|
207
|
-
|
|
208
|
-
for (const [capName, capConfig] of Object.entries(
|
|
209
|
-
config.capabilities || {}
|
|
210
|
-
)) {
|
|
211
|
-
lines.push(` ${capName}:`);
|
|
212
|
-
|
|
213
|
-
for (const providerName of capConfig.providers || []) {
|
|
214
|
-
const provider = config.providers?.[providerName];
|
|
215
|
-
if (!provider) continue;
|
|
216
|
-
|
|
217
|
-
let routing = 'unknown';
|
|
218
|
-
if (provider.type === 'cli') {
|
|
219
|
-
routing = provider.detected ? 'local' : 'devserver';
|
|
220
|
-
} else if (provider.type === 'api') {
|
|
221
|
-
routing = 'devserver';
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
lines.push(` - ${providerName} (${routing})`);
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
lines.push('');
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
return lines.join('\n');
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
/**
|
|
234
|
-
* Estimate costs
|
|
235
|
-
* @param {Object} config - Router config
|
|
236
|
-
* @param {Object} usage - Usage estimates
|
|
237
|
-
* @returns {Object} Cost estimates
|
|
238
|
-
*/
|
|
239
|
-
export function estimateCosts(config, usage = {}) {
|
|
240
|
-
const estimate = {};
|
|
241
|
-
|
|
242
|
-
for (const [capName, capConfig] of Object.entries(
|
|
243
|
-
config.capabilities || {}
|
|
244
|
-
)) {
|
|
245
|
-
const perDay = usage[`${capName}sPerDay`] || usage.reviewsPerDay || 10;
|
|
246
|
-
const tokens = AVG_TOKENS[capName] || AVG_TOKENS.review;
|
|
247
|
-
|
|
248
|
-
let localCost = 0;
|
|
249
|
-
let devserverCost = 0;
|
|
250
|
-
|
|
251
|
-
for (const providerName of capConfig.providers || []) {
|
|
252
|
-
const provider = config.providers?.[providerName];
|
|
253
|
-
if (!provider) continue;
|
|
254
|
-
|
|
255
|
-
if (provider.type === 'cli' && provider.detected) {
|
|
256
|
-
// Local CLI is free
|
|
257
|
-
localCost += 0;
|
|
258
|
-
} else {
|
|
259
|
-
// API or devserver costs money
|
|
260
|
-
const pricing = API_COSTS[providerName] || API_COSTS.default;
|
|
261
|
-
const inputCost = (tokens.input / 1000) * pricing.input * perDay * 30;
|
|
262
|
-
const outputCost = (tokens.output / 1000) * pricing.output * perDay * 30;
|
|
263
|
-
devserverCost += inputCost + outputCost;
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
estimate[capName] = {
|
|
268
|
-
local: localCost,
|
|
269
|
-
devserver: Math.round(devserverCost * 100) / 100,
|
|
270
|
-
};
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
return estimate;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
/**
|
|
277
|
-
* Save router config
|
|
278
|
-
* @param {string} projectDir - Project directory
|
|
279
|
-
* @param {Object} routerConfig - Router configuration
|
|
280
|
-
*/
|
|
281
|
-
export async function saveConfig(projectDir, routerConfig) {
|
|
282
|
-
const configPath = path.join(projectDir, '.tlc.json');
|
|
283
|
-
|
|
284
|
-
// Read existing config
|
|
285
|
-
let existingConfig = {};
|
|
286
|
-
try {
|
|
287
|
-
const content = await fs.readFile(configPath, 'utf8');
|
|
288
|
-
existingConfig = JSON.parse(content);
|
|
289
|
-
} catch (err) {
|
|
290
|
-
// No existing config
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
// Merge router config
|
|
294
|
-
const newConfig = {
|
|
295
|
-
...existingConfig,
|
|
296
|
-
router: routerConfig,
|
|
297
|
-
};
|
|
298
|
-
|
|
299
|
-
await fs.writeFile(configPath, JSON.stringify(newConfig, null, 2));
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
/**
|
|
303
|
-
* Build provider config from detected CLIs
|
|
304
|
-
* @param {Object} detected - Detected CLI info
|
|
305
|
-
* @returns {Object} Provider config
|
|
306
|
-
*/
|
|
307
|
-
function buildProviderConfig(detected) {
|
|
308
|
-
const providers = {};
|
|
309
|
-
|
|
310
|
-
// Add detected CLIs
|
|
311
|
-
if (detected.claude) {
|
|
312
|
-
providers.claude = {
|
|
313
|
-
type: 'cli',
|
|
314
|
-
command: 'claude',
|
|
315
|
-
detected: true,
|
|
316
|
-
headlessArgs: ['-p', '--output-format', 'json'],
|
|
317
|
-
capabilities: ['review', 'code-gen', 'refactor', 'explain'],
|
|
318
|
-
};
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
if (detected.codex) {
|
|
322
|
-
providers.codex = {
|
|
323
|
-
type: 'cli',
|
|
324
|
-
command: 'codex',
|
|
325
|
-
detected: true,
|
|
326
|
-
headlessArgs: ['exec', '--json', '--sandbox', 'read-only'],
|
|
327
|
-
capabilities: ['review', 'code-gen', 'refactor'],
|
|
328
|
-
};
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
if (detected.gemini) {
|
|
332
|
-
providers.gemini = {
|
|
333
|
-
type: 'cli',
|
|
334
|
-
command: 'gemini',
|
|
335
|
-
detected: true,
|
|
336
|
-
headlessArgs: ['-p', '--output-format', 'json'],
|
|
337
|
-
capabilities: ['design', 'vision', 'review', 'image-gen'],
|
|
338
|
-
};
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
// Always include API providers (devserver-only)
|
|
342
|
-
providers.deepseek = {
|
|
343
|
-
type: 'api',
|
|
344
|
-
baseUrl: 'https://api.deepseek.com',
|
|
345
|
-
model: 'deepseek-coder',
|
|
346
|
-
capabilities: ['review'],
|
|
347
|
-
devserverOnly: true,
|
|
348
|
-
};
|
|
349
|
-
|
|
350
|
-
return providers;
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
/**
|
|
354
|
-
* Build capability config from detected CLIs
|
|
355
|
-
* @param {Object} detected - Detected CLI info
|
|
356
|
-
* @returns {Object} Capability config
|
|
357
|
-
*/
|
|
358
|
-
function buildCapabilityConfig(detected) {
|
|
359
|
-
const capabilities = {};
|
|
360
|
-
|
|
361
|
-
// Review capability - use all available
|
|
362
|
-
const reviewProviders = [];
|
|
363
|
-
if (detected.claude) reviewProviders.push('claude');
|
|
364
|
-
if (detected.codex) reviewProviders.push('codex');
|
|
365
|
-
reviewProviders.push('deepseek'); // Always available via devserver
|
|
366
|
-
|
|
367
|
-
capabilities.review = {
|
|
368
|
-
providers: reviewProviders,
|
|
369
|
-
consensus: 'majority',
|
|
370
|
-
};
|
|
371
|
-
|
|
372
|
-
// Design capability - gemini only
|
|
373
|
-
if (detected.gemini) {
|
|
374
|
-
capabilities.design = {
|
|
375
|
-
providers: ['gemini'],
|
|
376
|
-
};
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
// Code generation - claude preferred
|
|
380
|
-
const codeGenProviders = [];
|
|
381
|
-
if (detected.claude) codeGenProviders.push('claude');
|
|
382
|
-
if (detected.codex) codeGenProviders.push('codex');
|
|
383
|
-
|
|
384
|
-
if (codeGenProviders.length > 0) {
|
|
385
|
-
capabilities['code-gen'] = {
|
|
386
|
-
providers: codeGenProviders,
|
|
387
|
-
};
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
return capabilities;
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
/**
|
|
394
|
-
* Build routing table from detected CLIs and devserver
|
|
395
|
-
* @param {Object} detected - Detected CLI info
|
|
396
|
-
* @param {Object} devserver - Devserver status
|
|
397
|
-
* @returns {Object} Routing table
|
|
398
|
-
*/
|
|
399
|
-
function buildRoutingTable(detected, devserver) {
|
|
400
|
-
const table = {};
|
|
401
|
-
|
|
402
|
-
// CLI providers
|
|
403
|
-
for (const name of ['claude', 'codex', 'gemini']) {
|
|
404
|
-
table[name] = {
|
|
405
|
-
local: detected[name]?.detected || false,
|
|
406
|
-
devserver: devserver.connected,
|
|
407
|
-
preferred: detected[name]?.detected ? 'local' : 'devserver',
|
|
408
|
-
};
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
// API providers
|
|
412
|
-
table.deepseek = {
|
|
413
|
-
local: false,
|
|
414
|
-
devserver: true,
|
|
415
|
-
preferred: 'devserver',
|
|
416
|
-
};
|
|
417
|
-
|
|
418
|
-
return table;
|
|
419
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Router Setup Command - Interactive setup for multi-model routing
|
|
3
|
+
* Phase 33, Task 10
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { detectAllCLIs, detectCLI } from './cli-detector.js';
|
|
7
|
+
import { writeFile } from 'fs/promises';
|
|
8
|
+
import { join } from 'path';
|
|
9
|
+
|
|
10
|
+
const PRICING = {
|
|
11
|
+
claude: { inputPer1k: 0.003, outputPer1k: 0.015 },
|
|
12
|
+
codex: { inputPer1k: 0.002, outputPer1k: 0.008 },
|
|
13
|
+
gemini: { inputPer1k: 0.001, outputPer1k: 0.002 },
|
|
14
|
+
deepseek: { inputPer1k: 0.0001, outputPer1k: 0.0002 },
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export class RouterSetup {
|
|
18
|
+
constructor() {
|
|
19
|
+
this.config = { providers: {}, capabilities: {}, devserver: {} };
|
|
20
|
+
this._detectAllCLIs = detectAllCLIs;
|
|
21
|
+
this._detectCLI = detectCLI;
|
|
22
|
+
this._fetch = globalThis.fetch;
|
|
23
|
+
this._writeFile = null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async detectCLIs() {
|
|
27
|
+
return this._detectAllCLIs();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async testDevserver(url) {
|
|
31
|
+
try {
|
|
32
|
+
const res = await this._fetch(url + '/health');
|
|
33
|
+
return { connected: res.ok };
|
|
34
|
+
} catch {
|
|
35
|
+
return { connected: false };
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
formatRoutingTable(providers) {
|
|
40
|
+
const lines = ['Provider Location'];
|
|
41
|
+
for (const [name, info] of Object.entries(providers)) {
|
|
42
|
+
lines.push(name.padEnd(12) + ' ' + info.location);
|
|
43
|
+
}
|
|
44
|
+
return lines.join('\n');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
estimateCosts(usage) {
|
|
48
|
+
let total = 0;
|
|
49
|
+
for (const [cap, count] of Object.entries(usage)) {
|
|
50
|
+
total += count * 0.01; // Simplified
|
|
51
|
+
}
|
|
52
|
+
return { total, breakdown: usage };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
configureProvider(name, config) {
|
|
56
|
+
this.config.providers[name] = config;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
configureCapability(name, providers) {
|
|
60
|
+
this.config.capabilities[name] = { providers };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async testProvider(name) {
|
|
64
|
+
const result = await this._detectCLI(name);
|
|
65
|
+
return { available: result.found };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
formatRoutingSummary(config) {
|
|
69
|
+
const lines = [];
|
|
70
|
+
for (const [name, info] of Object.entries(config.providers || {})) {
|
|
71
|
+
lines.push(name + ': ' + info.location);
|
|
72
|
+
}
|
|
73
|
+
return lines.join('\n');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
estimateCostsPerCapability(usage) {
|
|
77
|
+
const costs = {};
|
|
78
|
+
for (const [cap, info] of Object.entries(usage)) {
|
|
79
|
+
costs[cap] = info.count * info.avgTokens * 0.000001;
|
|
80
|
+
}
|
|
81
|
+
return costs;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async saveConfig() {
|
|
85
|
+
const json = JSON.stringify({ router: this.config }, null, 2);
|
|
86
|
+
if (this._writeFile) {
|
|
87
|
+
await this._writeFile(json);
|
|
88
|
+
} else {
|
|
89
|
+
await writeFile(join(process.cwd(), '.tlc.json'), json);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export default { RouterSetup };
|