tlc-claude-code 1.4.2 → 1.4.5
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/accessibility.test.d.ts +1 -0
- package/dashboard/dist/components/accessibility.test.js +116 -0
- package/dashboard/dist/components/layout/MobileNav.d.ts +16 -0
- package/dashboard/dist/components/layout/MobileNav.js +31 -0
- package/dashboard/dist/components/layout/MobileNav.test.d.ts +1 -0
- package/dashboard/dist/components/layout/MobileNav.test.js +111 -0
- package/dashboard/dist/components/performance.test.d.ts +1 -0
- package/dashboard/dist/components/performance.test.js +114 -0
- package/dashboard/dist/components/responsive.test.d.ts +1 -0
- package/dashboard/dist/components/responsive.test.js +114 -0
- package/dashboard/dist/components/ui/Dropdown.d.ts +22 -0
- package/dashboard/dist/components/ui/Dropdown.js +109 -0
- package/dashboard/dist/components/ui/Dropdown.test.d.ts +1 -0
- package/dashboard/dist/components/ui/Dropdown.test.js +105 -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/dist/components/ui/Modal.d.ts +13 -0
- package/dashboard/dist/components/ui/Modal.js +25 -0
- package/dashboard/dist/components/ui/Modal.test.d.ts +1 -0
- package/dashboard/dist/components/ui/Modal.test.js +91 -0
- package/dashboard/dist/components/ui/Skeleton.d.ts +32 -0
- package/dashboard/dist/components/ui/Skeleton.js +48 -0
- package/dashboard/dist/components/ui/Skeleton.test.d.ts +1 -0
- package/dashboard/dist/components/ui/Skeleton.test.js +125 -0
- package/dashboard/dist/components/ui/Toast.d.ts +32 -0
- package/dashboard/dist/components/ui/Toast.js +21 -0
- package/dashboard/dist/components/ui/Toast.test.d.ts +1 -0
- package/dashboard/dist/components/ui/Toast.test.js +118 -0
- package/dashboard/dist/hooks/useTheme.d.ts +37 -0
- package/dashboard/dist/hooks/useTheme.js +96 -0
- package/dashboard/dist/hooks/useTheme.test.d.ts +1 -0
- package/dashboard/dist/hooks/useTheme.test.js +94 -0
- package/dashboard/dist/hooks/useWebSocket.d.ts +17 -0
- package/dashboard/dist/hooks/useWebSocket.js +100 -0
- package/dashboard/dist/hooks/useWebSocket.test.d.ts +1 -0
- package/dashboard/dist/hooks/useWebSocket.test.js +115 -0
- package/dashboard/dist/stores/projectStore.d.ts +44 -0
- package/dashboard/dist/stores/projectStore.js +76 -0
- package/dashboard/dist/stores/projectStore.test.d.ts +1 -0
- package/dashboard/dist/stores/projectStore.test.js +114 -0
- package/dashboard/dist/stores/uiStore.d.ts +29 -0
- package/dashboard/dist/stores/uiStore.js +72 -0
- package/dashboard/dist/stores/uiStore.test.d.ts +1 -0
- package/dashboard/dist/stores/uiStore.test.js +93 -0
- package/dashboard/package.json +6 -3
- package/docker-compose.dev.yml +6 -1
- package/package.json +1 -1
- package/server/dashboard/index.html +1545 -791
- package/server/index.js +64 -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 +1 -1
- 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/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,221 +1,99 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Router Config - Configuration
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
import
|
|
7
|
-
|
|
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
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Get provider configuration by name
|
|
102
|
-
* @param {Object} config - Router config
|
|
103
|
-
* @param {string} name - Provider name
|
|
104
|
-
* @returns {Object|null} Provider config or null
|
|
105
|
-
*/
|
|
106
|
-
export function getProviderConfig(config, name) {
|
|
107
|
-
return config.providers?.[name] || null;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Get capability configuration by name
|
|
112
|
-
* @param {Object} config - Router config
|
|
113
|
-
* @param {string} name - Capability name
|
|
114
|
-
* @returns {Object|null} Capability config or null
|
|
115
|
-
*/
|
|
116
|
-
export function getCapabilityConfig(config, name) {
|
|
117
|
-
return config.capabilities?.[name] || null;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Migrate old config format to new format
|
|
122
|
-
* @param {Object} config - Possibly old format config
|
|
123
|
-
* @returns {Object} New format config
|
|
124
|
-
*/
|
|
125
|
-
export function migrateConfig(config) {
|
|
126
|
-
// If already has router section, extract it
|
|
127
|
-
if (config.router) {
|
|
128
|
-
return {
|
|
129
|
-
providers: {
|
|
130
|
-
...defaultConfig.providers,
|
|
131
|
-
...(config.router.providers || {}),
|
|
132
|
-
},
|
|
133
|
-
capabilities: {
|
|
134
|
-
...defaultConfig.capabilities,
|
|
135
|
-
...(config.router.capabilities || {}),
|
|
136
|
-
},
|
|
137
|
-
devserver: {
|
|
138
|
-
...defaultConfig.devserver,
|
|
139
|
-
...(config.router.devserver || {}),
|
|
140
|
-
},
|
|
141
|
-
};
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// Handle old adapter-based format
|
|
145
|
-
if (config.adapters) {
|
|
146
|
-
const providers = {};
|
|
147
|
-
|
|
148
|
-
for (const [name, adapter] of Object.entries(config.adapters)) {
|
|
149
|
-
providers[name] = {
|
|
150
|
-
type: adapter.type || 'cli',
|
|
151
|
-
command: adapter.command || name,
|
|
152
|
-
capabilities: adapter.capabilities || [],
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
return {
|
|
157
|
-
providers: {
|
|
158
|
-
...defaultConfig.providers,
|
|
159
|
-
...providers,
|
|
160
|
-
},
|
|
161
|
-
capabilities: defaultConfig.capabilities,
|
|
162
|
-
devserver: defaultConfig.devserver,
|
|
163
|
-
};
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// Return defaults
|
|
167
|
-
return defaultConfig;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* Load router configuration from .tlc.json
|
|
172
|
-
* @param {string} projectDir - Project directory
|
|
173
|
-
* @returns {Promise<Object>} Router configuration
|
|
174
|
-
*/
|
|
175
|
-
export async function loadRouterConfig(projectDir) {
|
|
176
|
-
const configPath = path.join(projectDir, '.tlc.json');
|
|
177
|
-
|
|
178
|
-
let fileConfig = {};
|
|
179
|
-
try {
|
|
180
|
-
const content = await fs.readFile(configPath, 'utf8');
|
|
181
|
-
fileConfig = JSON.parse(content);
|
|
182
|
-
} catch (err) {
|
|
183
|
-
// File doesn't exist, use defaults
|
|
184
|
-
return defaultConfig;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// Migrate if needed
|
|
188
|
-
const config = migrateConfig(fileConfig);
|
|
189
|
-
|
|
190
|
-
// Validate
|
|
191
|
-
validateProviders(config.providers);
|
|
192
|
-
validateCapabilities(config);
|
|
193
|
-
|
|
194
|
-
return config;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* Save router configuration to .tlc.json
|
|
199
|
-
* @param {string} projectDir - Project directory
|
|
200
|
-
* @param {Object} routerConfig - Router configuration
|
|
201
|
-
*/
|
|
202
|
-
export async function saveRouterConfig(projectDir, routerConfig) {
|
|
203
|
-
const configPath = path.join(projectDir, '.tlc.json');
|
|
204
|
-
|
|
205
|
-
// Read existing config
|
|
206
|
-
let existingConfig = {};
|
|
207
|
-
try {
|
|
208
|
-
const content = await fs.readFile(configPath, 'utf8');
|
|
209
|
-
existingConfig = JSON.parse(content);
|
|
210
|
-
} catch (err) {
|
|
211
|
-
// File doesn't exist
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
// Merge router config
|
|
215
|
-
const newConfig = {
|
|
216
|
-
...existingConfig,
|
|
217
|
-
router: routerConfig,
|
|
218
|
-
};
|
|
219
|
-
|
|
220
|
-
await fs.writeFile(configPath, JSON.stringify(newConfig, null, 2));
|
|
221
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Router Config Schema - Configuration for .tlc.json router section
|
|
3
|
+
* Phase 33, Task 9
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { readFile, writeFile } from 'fs/promises';
|
|
7
|
+
import { join } from 'path';
|
|
8
|
+
|
|
9
|
+
export const defaultConfig = {
|
|
10
|
+
providers: {
|
|
11
|
+
claude: { type: 'cli', command: 'claude', capabilities: ['review', 'code-gen', 'refactor'] },
|
|
12
|
+
codex: { type: 'cli', command: 'codex', capabilities: ['review', 'code-gen'] },
|
|
13
|
+
gemini: { type: 'cli', command: 'gemini', capabilities: ['design', 'vision'] },
|
|
14
|
+
},
|
|
15
|
+
capabilities: {
|
|
16
|
+
review: { providers: ['claude', 'codex'] },
|
|
17
|
+
'code-gen': { providers: ['claude'] },
|
|
18
|
+
design: { providers: ['gemini'] },
|
|
19
|
+
},
|
|
20
|
+
devserver: { url: null, queue: { maxConcurrent: 3, timeout: 120000 } },
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export async function loadRouterConfig(options = {}) {
|
|
24
|
+
try {
|
|
25
|
+
let content;
|
|
26
|
+
if (options._readFile) {
|
|
27
|
+
content = await options._readFile();
|
|
28
|
+
} else {
|
|
29
|
+
const configPath = join(process.cwd(), '.tlc.json');
|
|
30
|
+
content = await readFile(configPath, 'utf-8');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const data = JSON.parse(content);
|
|
34
|
+
const routerConfig = data.router || {};
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
providers: { ...defaultConfig.providers, ...routerConfig.providers },
|
|
38
|
+
capabilities: { ...defaultConfig.capabilities, ...routerConfig.capabilities },
|
|
39
|
+
devserver: { ...defaultConfig.devserver, ...routerConfig.devserver },
|
|
40
|
+
};
|
|
41
|
+
} catch {
|
|
42
|
+
return { ...defaultConfig };
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function validateConfig(config) {
|
|
47
|
+
const errors = [];
|
|
48
|
+
|
|
49
|
+
// Check capability provider references
|
|
50
|
+
if (config.capabilities) {
|
|
51
|
+
for (const [cap, capConfig] of Object.entries(config.capabilities)) {
|
|
52
|
+
for (const provider of capConfig.providers || []) {
|
|
53
|
+
if (!config.providers?.[provider]) {
|
|
54
|
+
errors.push('Capability ' + cap + ' references unknown provider: ' + provider);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return { valid: errors.length === 0, errors };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function getProviderConfig(config, name) {
|
|
64
|
+
return config.providers?.[name] || null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function getCapabilityConfig(config, name) {
|
|
68
|
+
return config.capabilities?.[name] || null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export async function saveRouterConfig(config, options = {}) {
|
|
72
|
+
const configPath = join(process.cwd(), '.tlc.json');
|
|
73
|
+
|
|
74
|
+
let existing = {};
|
|
75
|
+
try {
|
|
76
|
+
const content = await readFile(configPath, 'utf-8');
|
|
77
|
+
existing = JSON.parse(content);
|
|
78
|
+
} catch {
|
|
79
|
+
// New file
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
existing.router = config;
|
|
83
|
+
const json = JSON.stringify(existing, null, 2);
|
|
84
|
+
|
|
85
|
+
if (options._writeFile) {
|
|
86
|
+
await options._writeFile(json);
|
|
87
|
+
} else {
|
|
88
|
+
await writeFile(configPath, json);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export default {
|
|
93
|
+
loadRouterConfig,
|
|
94
|
+
validateConfig,
|
|
95
|
+
getProviderConfig,
|
|
96
|
+
getCapabilityConfig,
|
|
97
|
+
defaultConfig,
|
|
98
|
+
saveRouterConfig,
|
|
99
|
+
};
|
|
@@ -1,237 +1,83 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
-
import {
|
|
3
|
-
loadRouterConfig,
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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
|
-
expect(
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
describe('validateProviders', () => {
|
|
86
|
-
it('checks required fields for CLI', () => {
|
|
87
|
-
const providers = {
|
|
88
|
-
claude: { type: 'cli', command: 'claude' },
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
expect(() => validateProviders(providers)).not.toThrow();
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
it('throws on missing command for CLI', () => {
|
|
95
|
-
const providers = {
|
|
96
|
-
claude: { type: 'cli' },
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
expect(() => validateProviders(providers)).toThrow(/command/i);
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
it('checks required fields for API', () => {
|
|
103
|
-
const providers = {
|
|
104
|
-
deepseek: { type: 'api', baseUrl: 'https://api.deepseek.com' },
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
expect(() => validateProviders(providers)).not.toThrow();
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
it('throws on missing baseUrl for API', () => {
|
|
111
|
-
const providers = {
|
|
112
|
-
deepseek: { type: 'api' },
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
expect(() => validateProviders(providers)).toThrow(/baseUrl/i);
|
|
116
|
-
});
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
describe('getProviderConfig', () => {
|
|
120
|
-
it('returns provider config', () => {
|
|
121
|
-
const config = {
|
|
122
|
-
providers: {
|
|
123
|
-
claude: { type: 'cli', command: 'claude', capabilities: ['review'] },
|
|
124
|
-
},
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
const provider = getProviderConfig(config, 'claude');
|
|
128
|
-
|
|
129
|
-
expect(provider.type).toBe('cli');
|
|
130
|
-
expect(provider.command).toBe('claude');
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
it('returns null for unknown provider', () => {
|
|
134
|
-
const config = { providers: {} };
|
|
135
|
-
|
|
136
|
-
const provider = getProviderConfig(config, 'unknown');
|
|
137
|
-
|
|
138
|
-
expect(provider).toBeNull();
|
|
139
|
-
});
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
describe('getCapabilityConfig', () => {
|
|
143
|
-
it('returns providers array', () => {
|
|
144
|
-
const config = {
|
|
145
|
-
capabilities: {
|
|
146
|
-
review: { providers: ['claude', 'codex'] },
|
|
147
|
-
},
|
|
148
|
-
};
|
|
149
|
-
|
|
150
|
-
const cap = getCapabilityConfig(config, 'review');
|
|
151
|
-
|
|
152
|
-
expect(cap.providers).toEqual(['claude', 'codex']);
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
it('returns null for unknown capability', () => {
|
|
156
|
-
const config = { capabilities: {} };
|
|
157
|
-
|
|
158
|
-
const cap = getCapabilityConfig(config, 'unknown');
|
|
159
|
-
|
|
160
|
-
expect(cap).toBeNull();
|
|
161
|
-
});
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
describe('migrateConfig', () => {
|
|
165
|
-
it('handles old format', () => {
|
|
166
|
-
const oldConfig = {
|
|
167
|
-
model: 'claude', // Old format
|
|
168
|
-
adapters: { claude: {} },
|
|
169
|
-
};
|
|
170
|
-
|
|
171
|
-
const migrated = migrateConfig(oldConfig);
|
|
172
|
-
|
|
173
|
-
expect(migrated.providers).toBeDefined();
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
it('preserves new format', () => {
|
|
177
|
-
const newConfig = {
|
|
178
|
-
router: {
|
|
179
|
-
providers: { claude: { type: 'cli', command: 'claude' } },
|
|
180
|
-
},
|
|
181
|
-
};
|
|
182
|
-
|
|
183
|
-
const migrated = migrateConfig(newConfig);
|
|
184
|
-
|
|
185
|
-
expect(migrated.providers.claude.type).toBe('cli');
|
|
186
|
-
});
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
describe('defaultConfig', () => {
|
|
190
|
-
it('has sensible defaults', () => {
|
|
191
|
-
expect(defaultConfig.providers).toBeDefined();
|
|
192
|
-
expect(defaultConfig.capabilities).toBeDefined();
|
|
193
|
-
expect(defaultConfig.devserver).toBeDefined();
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
it('includes all standard providers', () => {
|
|
197
|
-
expect(defaultConfig.providers.claude).toBeDefined();
|
|
198
|
-
expect(defaultConfig.providers.codex).toBeDefined();
|
|
199
|
-
expect(defaultConfig.providers.gemini).toBeDefined();
|
|
200
|
-
expect(defaultConfig.providers.deepseek).toBeDefined();
|
|
201
|
-
});
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
describe('saveRouterConfig', () => {
|
|
205
|
-
it('writes to file', async () => {
|
|
206
|
-
fs.readFile.mockResolvedValue('{}');
|
|
207
|
-
fs.writeFile.mockResolvedValue();
|
|
208
|
-
|
|
209
|
-
const config = {
|
|
210
|
-
providers: { claude: { type: 'cli', command: 'claude' } },
|
|
211
|
-
};
|
|
212
|
-
|
|
213
|
-
await saveRouterConfig('/project', config);
|
|
214
|
-
|
|
215
|
-
expect(fs.writeFile).toHaveBeenCalled();
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
it('merges with existing config', async () => {
|
|
219
|
-
fs.readFile.mockResolvedValue(JSON.stringify({
|
|
220
|
-
testFrameworks: { primary: 'vitest' },
|
|
221
|
-
}));
|
|
222
|
-
fs.writeFile.mockResolvedValue();
|
|
223
|
-
|
|
224
|
-
const routerConfig = {
|
|
225
|
-
providers: { claude: { type: 'cli', command: 'claude' } },
|
|
226
|
-
};
|
|
227
|
-
|
|
228
|
-
await saveRouterConfig('/project', routerConfig);
|
|
229
|
-
|
|
230
|
-
const writeCall = fs.writeFile.mock.calls[0];
|
|
231
|
-
const written = JSON.parse(writeCall[1]);
|
|
232
|
-
|
|
233
|
-
expect(written.testFrameworks.primary).toBe('vitest');
|
|
234
|
-
expect(written.router.providers.claude).toBeDefined();
|
|
235
|
-
});
|
|
236
|
-
});
|
|
237
|
-
});
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
loadRouterConfig,
|
|
4
|
+
validateConfig,
|
|
5
|
+
getProviderConfig,
|
|
6
|
+
getCapabilityConfig,
|
|
7
|
+
defaultConfig,
|
|
8
|
+
saveRouterConfig,
|
|
9
|
+
} from './router-config.js';
|
|
10
|
+
|
|
11
|
+
describe('Router Config', () => {
|
|
12
|
+
describe('loadRouterConfig', () => {
|
|
13
|
+
it('reads from .tlc.json', async () => {
|
|
14
|
+
const config = await loadRouterConfig({ _readFile: vi.fn().mockResolvedValue(JSON.stringify({
|
|
15
|
+
router: { providers: { test: { type: 'cli' } } }
|
|
16
|
+
}))});
|
|
17
|
+
expect(config.providers).toHaveProperty('test');
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('validates schema', async () => {
|
|
21
|
+
const config = await loadRouterConfig({ _readFile: vi.fn().mockResolvedValue(JSON.stringify({
|
|
22
|
+
router: { providers: {} }
|
|
23
|
+
}))});
|
|
24
|
+
expect(config).toBeDefined();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('merges defaults', async () => {
|
|
28
|
+
const config = await loadRouterConfig({ _readFile: vi.fn().mockResolvedValue('{}') });
|
|
29
|
+
expect(config.providers).toBeDefined();
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
describe('validateConfig', () => {
|
|
34
|
+
it('checks provider refs', () => {
|
|
35
|
+
const result = validateConfig({
|
|
36
|
+
capabilities: { review: { providers: ['nonexistent'] } },
|
|
37
|
+
providers: {},
|
|
38
|
+
});
|
|
39
|
+
expect(result.valid).toBe(false);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('checks required fields', () => {
|
|
43
|
+
const result = validateConfig({
|
|
44
|
+
providers: { test: { type: 'cli' } },
|
|
45
|
+
capabilities: {},
|
|
46
|
+
});
|
|
47
|
+
expect(result.valid).toBe(true);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
describe('getProviderConfig', () => {
|
|
52
|
+
it('returns provider', () => {
|
|
53
|
+
const config = { providers: { claude: { type: 'cli', command: 'claude' } } };
|
|
54
|
+
const provider = getProviderConfig(config, 'claude');
|
|
55
|
+
expect(provider.type).toBe('cli');
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
describe('getCapabilityConfig', () => {
|
|
60
|
+
it('returns providers array', () => {
|
|
61
|
+
const config = {
|
|
62
|
+
capabilities: { review: { providers: ['claude', 'codex'] } },
|
|
63
|
+
};
|
|
64
|
+
const cap = getCapabilityConfig(config, 'review');
|
|
65
|
+
expect(cap.providers).toHaveLength(2);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
describe('defaultConfig', () => {
|
|
70
|
+
it('has sensible defaults', () => {
|
|
71
|
+
expect(defaultConfig.providers).toBeDefined();
|
|
72
|
+
expect(defaultConfig.capabilities).toBeDefined();
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
describe('saveRouterConfig', () => {
|
|
77
|
+
it('writes to file', async () => {
|
|
78
|
+
const writeFile = vi.fn().mockResolvedValue(undefined);
|
|
79
|
+
await saveRouterConfig({ providers: {} }, { _writeFile: writeFile });
|
|
80
|
+
expect(writeFile).toHaveBeenCalled();
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
});
|