tlc-claude-code 1.4.4 → 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.
Files changed (70) hide show
  1. package/dashboard/dist/App.js +28 -2
  2. package/dashboard/dist/api/health-diagnostics.d.ts +26 -0
  3. package/dashboard/dist/api/health-diagnostics.js +85 -0
  4. package/dashboard/dist/api/health-diagnostics.test.d.ts +1 -0
  5. package/dashboard/dist/api/health-diagnostics.test.js +126 -0
  6. package/dashboard/dist/api/index.d.ts +5 -0
  7. package/dashboard/dist/api/index.js +5 -0
  8. package/dashboard/dist/api/notes-api.d.ts +18 -0
  9. package/dashboard/dist/api/notes-api.js +68 -0
  10. package/dashboard/dist/api/notes-api.test.d.ts +1 -0
  11. package/dashboard/dist/api/notes-api.test.js +113 -0
  12. package/dashboard/dist/api/safeFetch.d.ts +50 -0
  13. package/dashboard/dist/api/safeFetch.js +135 -0
  14. package/dashboard/dist/api/safeFetch.test.d.ts +1 -0
  15. package/dashboard/dist/api/safeFetch.test.js +215 -0
  16. package/dashboard/dist/api/tasks-api.d.ts +32 -0
  17. package/dashboard/dist/api/tasks-api.js +98 -0
  18. package/dashboard/dist/api/tasks-api.test.d.ts +1 -0
  19. package/dashboard/dist/api/tasks-api.test.js +383 -0
  20. package/dashboard/dist/components/BugsPane.d.ts +20 -0
  21. package/dashboard/dist/components/BugsPane.js +210 -0
  22. package/dashboard/dist/components/BugsPane.test.d.ts +1 -0
  23. package/dashboard/dist/components/BugsPane.test.js +256 -0
  24. package/dashboard/dist/components/HealthPane.d.ts +3 -1
  25. package/dashboard/dist/components/HealthPane.js +44 -6
  26. package/dashboard/dist/components/HealthPane.test.js +105 -2
  27. package/dashboard/dist/components/RouterPane.d.ts +4 -3
  28. package/dashboard/dist/components/RouterPane.js +60 -57
  29. package/dashboard/dist/components/RouterPane.test.js +150 -96
  30. package/dashboard/dist/components/UpdateBanner.d.ts +26 -0
  31. package/dashboard/dist/components/UpdateBanner.js +30 -0
  32. package/dashboard/dist/components/UpdateBanner.test.d.ts +1 -0
  33. package/dashboard/dist/components/UpdateBanner.test.js +96 -0
  34. package/dashboard/dist/components/ui/EmptyState.d.ts +14 -0
  35. package/dashboard/dist/components/ui/EmptyState.js +58 -0
  36. package/dashboard/dist/components/ui/EmptyState.test.d.ts +1 -0
  37. package/dashboard/dist/components/ui/EmptyState.test.js +97 -0
  38. package/dashboard/dist/components/ui/ErrorState.d.ts +17 -0
  39. package/dashboard/dist/components/ui/ErrorState.js +80 -0
  40. package/dashboard/dist/components/ui/ErrorState.test.d.ts +1 -0
  41. package/dashboard/dist/components/ui/ErrorState.test.js +166 -0
  42. package/dashboard/package.json +3 -0
  43. package/package.json +1 -1
  44. package/server/dashboard/index.html +205 -8
  45. package/server/index.js +64 -0
  46. package/server/lib/api-provider.js +104 -186
  47. package/server/lib/api-provider.test.js +238 -336
  48. package/server/lib/cli-detector.js +90 -166
  49. package/server/lib/cli-detector.test.js +114 -269
  50. package/server/lib/cli-provider.js +142 -212
  51. package/server/lib/cli-provider.test.js +196 -349
  52. package/server/lib/debug.test.js +1 -1
  53. package/server/lib/devserver-router-api.js +54 -249
  54. package/server/lib/devserver-router-api.test.js +126 -426
  55. package/server/lib/introspect.js +309 -0
  56. package/server/lib/introspect.test.js +286 -0
  57. package/server/lib/model-router.js +107 -245
  58. package/server/lib/model-router.test.js +122 -313
  59. package/server/lib/output-schemas.js +146 -269
  60. package/server/lib/output-schemas.test.js +106 -307
  61. package/server/lib/provider-interface.js +99 -153
  62. package/server/lib/provider-interface.test.js +228 -394
  63. package/server/lib/provider-queue.js +164 -158
  64. package/server/lib/provider-queue.test.js +186 -315
  65. package/server/lib/router-config.js +99 -221
  66. package/server/lib/router-config.test.js +83 -237
  67. package/server/lib/router-setup-command.js +94 -419
  68. package/server/lib/router-setup-command.test.js +96 -375
  69. package/server/lib/router-status-api.js +93 -0
  70. package/server/lib/router-status-api.test.js +270 -0
@@ -1,426 +1,126 @@
1
- import { describe, it, expect, vi, beforeEach } from 'vitest';
2
- import {
3
- createRouterAPI,
4
- handleRun,
5
- handleTaskStatus,
6
- handleReview,
7
- handleDesign,
8
- handleHealth,
9
- validateAuth,
10
- validateRequestBody,
11
- } from './devserver-router-api.js';
12
-
13
- // Mock dependencies
14
- vi.mock('./model-router.js', () => ({
15
- createRouter: vi.fn(),
16
- }));
17
-
18
- vi.mock('./provider-queue.js', () => ({
19
- createQueue: vi.fn(),
20
- }));
21
-
22
- import { createRouter } from './model-router.js';
23
- import { createQueue } from './provider-queue.js';
24
-
25
- describe('devserver-router-api', () => {
26
- beforeEach(() => {
27
- vi.clearAllMocks();
28
- });
29
-
30
- describe('createRouterAPI', () => {
31
- it('creates API with route handlers', async () => {
32
- createRouter.mockResolvedValue({ run: vi.fn() });
33
- createQueue.mockReturnValue({ enqueue: vi.fn() });
34
-
35
- const api = await createRouterAPI({ secret: 'test-secret' });
36
-
37
- expect(api).toBeDefined();
38
- expect(api.handleRun).toBeDefined();
39
- expect(api.handleTaskStatus).toBeDefined();
40
- expect(api.handleReview).toBeDefined();
41
- expect(api.handleDesign).toBeDefined();
42
- expect(api.handleHealth).toBeDefined();
43
- });
44
-
45
- it('initializes router and queue', async () => {
46
- createRouter.mockResolvedValue({ run: vi.fn() });
47
- createQueue.mockReturnValue({ enqueue: vi.fn() });
48
-
49
- await createRouterAPI({ secret: 'test-secret' });
50
-
51
- expect(createRouter).toHaveBeenCalled();
52
- expect(createQueue).toHaveBeenCalled();
53
- });
54
- });
55
-
56
- describe('handleRun', () => {
57
- it('queues task and returns taskId', async () => {
58
- const mockQueue = {
59
- enqueue: vi.fn().mockResolvedValue('task-123'),
60
- };
61
- const mockRouter = { run: vi.fn() };
62
-
63
- const handler = handleRun(mockRouter, mockQueue);
64
- const req = {
65
- body: {
66
- capability: 'review',
67
- prompt: 'Review this code',
68
- },
69
- };
70
- const res = {
71
- json: vi.fn(),
72
- status: vi.fn().mockReturnThis(),
73
- };
74
-
75
- await handler(req, res);
76
-
77
- expect(mockQueue.enqueue).toHaveBeenCalled();
78
- expect(res.json).toHaveBeenCalledWith(
79
- expect.objectContaining({ taskId: 'task-123' })
80
- );
81
- });
82
-
83
- it('returns taskId in response', async () => {
84
- const mockQueue = {
85
- enqueue: vi.fn().mockResolvedValue('task-456'),
86
- };
87
- const mockRouter = { run: vi.fn() };
88
-
89
- const handler = handleRun(mockRouter, mockQueue);
90
- const req = {
91
- body: { capability: 'code-gen', prompt: 'Generate code' },
92
- };
93
- const res = {
94
- json: vi.fn(),
95
- status: vi.fn().mockReturnThis(),
96
- };
97
-
98
- await handler(req, res);
99
-
100
- expect(res.json).toHaveBeenCalledWith({ taskId: 'task-456' });
101
- });
102
- });
103
-
104
- describe('handleTaskStatus', () => {
105
- it('returns pending status for queued task', async () => {
106
- const mockQueue = {
107
- getTask: vi.fn().mockReturnValue({ status: 'pending' }),
108
- };
109
-
110
- const handler = handleTaskStatus(mockQueue);
111
- const req = { params: { taskId: 'task-123' } };
112
- const res = {
113
- json: vi.fn(),
114
- status: vi.fn().mockReturnThis(),
115
- };
116
-
117
- await handler(req, res);
118
-
119
- expect(res.json).toHaveBeenCalledWith(
120
- expect.objectContaining({ status: 'pending' })
121
- );
122
- });
123
-
124
- it('returns completed result for finished task', async () => {
125
- const mockQueue = {
126
- getTask: vi.fn().mockReturnValue({
127
- status: 'completed',
128
- result: { summary: 'Code looks good' },
129
- }),
130
- };
131
-
132
- const handler = handleTaskStatus(mockQueue);
133
- const req = { params: { taskId: 'task-123' } };
134
- const res = {
135
- json: vi.fn(),
136
- status: vi.fn().mockReturnThis(),
137
- };
138
-
139
- await handler(req, res);
140
-
141
- expect(res.json).toHaveBeenCalledWith(
142
- expect.objectContaining({
143
- status: 'completed',
144
- result: { summary: 'Code looks good' },
145
- })
146
- );
147
- });
148
-
149
- it('returns 404 for unknown task', async () => {
150
- const mockQueue = {
151
- getTask: vi.fn().mockReturnValue(null),
152
- };
153
-
154
- const handler = handleTaskStatus(mockQueue);
155
- const req = { params: { taskId: 'unknown' } };
156
- const res = {
157
- json: vi.fn(),
158
- status: vi.fn().mockReturnThis(),
159
- };
160
-
161
- await handler(req, res);
162
-
163
- expect(res.status).toHaveBeenCalledWith(404);
164
- });
165
- });
166
-
167
- describe('handleReview', () => {
168
- it('runs multiple providers for review', async () => {
169
- const mockRouter = {
170
- run: vi.fn().mockResolvedValue([
171
- { provider: 'claude', success: true, result: { score: 85 } },
172
- { provider: 'codex', success: true, result: { score: 80 } },
173
- ]),
174
- };
175
-
176
- const handler = handleReview(mockRouter);
177
- const req = {
178
- body: { code: 'function test() {}', prompt: 'Review this' },
179
- };
180
- const res = {
181
- json: vi.fn(),
182
- status: vi.fn().mockReturnThis(),
183
- };
184
-
185
- await handler(req, res);
186
-
187
- expect(mockRouter.run).toHaveBeenCalledWith(
188
- 'review',
189
- expect.any(String),
190
- expect.any(Object)
191
- );
192
- });
193
-
194
- it('returns consensus from multiple providers', async () => {
195
- const mockRouter = {
196
- run: vi.fn().mockResolvedValue([
197
- { provider: 'claude', success: true, result: { approved: true } },
198
- { provider: 'codex', success: true, result: { approved: true } },
199
- { provider: 'deepseek', success: true, result: { approved: false } },
200
- ]),
201
- };
202
-
203
- const handler = handleReview(mockRouter);
204
- const req = {
205
- body: { code: 'function test() {}' },
206
- };
207
- const res = {
208
- json: vi.fn(),
209
- status: vi.fn().mockReturnThis(),
210
- };
211
-
212
- await handler(req, res);
213
-
214
- expect(res.json).toHaveBeenCalledWith(
215
- expect.objectContaining({
216
- consensus: expect.any(Object),
217
- results: expect.any(Array),
218
- })
219
- );
220
- });
221
- });
222
-
223
- describe('handleDesign', () => {
224
- it('routes to gemini for design', async () => {
225
- const mockRouter = {
226
- run: vi.fn().mockResolvedValue([
227
- { provider: 'gemini', success: true, result: { mockups: [] } },
228
- ]),
229
- };
230
-
231
- const handler = handleDesign(mockRouter);
232
- const req = {
233
- body: { prompt: 'Design a login page' },
234
- };
235
- const res = {
236
- json: vi.fn(),
237
- status: vi.fn().mockReturnThis(),
238
- };
239
-
240
- await handler(req, res);
241
-
242
- expect(mockRouter.run).toHaveBeenCalledWith(
243
- 'design',
244
- expect.any(String),
245
- expect.any(Object)
246
- );
247
- });
248
-
249
- it('returns design result', async () => {
250
- const mockRouter = {
251
- run: vi.fn().mockResolvedValue([
252
- {
253
- provider: 'gemini',
254
- success: true,
255
- result: { mockups: ['mockup1.png'], rationale: 'Clean design' },
256
- },
257
- ]),
258
- };
259
-
260
- const handler = handleDesign(mockRouter);
261
- const req = {
262
- body: { prompt: 'Design a dashboard' },
263
- };
264
- const res = {
265
- json: vi.fn(),
266
- status: vi.fn().mockReturnThis(),
267
- };
268
-
269
- await handler(req, res);
270
-
271
- expect(res.json).toHaveBeenCalledWith(
272
- expect.objectContaining({
273
- result: expect.objectContaining({ mockups: expect.any(Array) }),
274
- })
275
- );
276
- });
277
- });
278
-
279
- describe('handleHealth', () => {
280
- it('shows provider availability', async () => {
281
- const mockRouter = {
282
- getStatus: vi.fn().mockReturnValue({
283
- providers: {
284
- claude: { detected: true, type: 'cli' },
285
- codex: { detected: false, type: 'cli' },
286
- deepseek: { detected: false, type: 'api' },
287
- },
288
- devserver: { configured: true },
289
- }),
290
- };
291
-
292
- const handler = handleHealth(mockRouter);
293
- const req = {};
294
- const res = {
295
- json: vi.fn(),
296
- status: vi.fn().mockReturnThis(),
297
- };
298
-
299
- await handler(req, res);
300
-
301
- expect(res.json).toHaveBeenCalledWith(
302
- expect.objectContaining({
303
- providers: expect.any(Object),
304
- devserver: expect.any(Object),
305
- })
306
- );
307
- });
308
-
309
- it('returns healthy status', async () => {
310
- const mockRouter = {
311
- getStatus: vi.fn().mockReturnValue({
312
- providers: { claude: { detected: true } },
313
- devserver: { configured: true },
314
- }),
315
- };
316
-
317
- const handler = handleHealth(mockRouter);
318
- const req = {};
319
- const res = {
320
- json: vi.fn(),
321
- status: vi.fn().mockReturnThis(),
322
- };
323
-
324
- await handler(req, res);
325
-
326
- expect(res.json).toHaveBeenCalledWith(
327
- expect.objectContaining({ healthy: true })
328
- );
329
- });
330
- });
331
-
332
- describe('validateAuth', () => {
333
- it('rejects unauthenticated requests', () => {
334
- const middleware = validateAuth('secret-key');
335
- const req = { headers: {} };
336
- const res = {
337
- json: vi.fn(),
338
- status: vi.fn().mockReturnThis(),
339
- };
340
- const next = vi.fn();
341
-
342
- middleware(req, res, next);
343
-
344
- expect(res.status).toHaveBeenCalledWith(401);
345
- expect(next).not.toHaveBeenCalled();
346
- });
347
-
348
- it('accepts valid auth header', () => {
349
- const middleware = validateAuth('secret-key');
350
- const req = {
351
- headers: { authorization: 'Bearer secret-key' },
352
- };
353
- const res = {
354
- json: vi.fn(),
355
- status: vi.fn().mockReturnThis(),
356
- };
357
- const next = vi.fn();
358
-
359
- middleware(req, res, next);
360
-
361
- expect(next).toHaveBeenCalled();
362
- });
363
-
364
- it('rejects invalid auth header', () => {
365
- const middleware = validateAuth('secret-key');
366
- const req = {
367
- headers: { authorization: 'Bearer wrong-key' },
368
- };
369
- const res = {
370
- json: vi.fn(),
371
- status: vi.fn().mockReturnThis(),
372
- };
373
- const next = vi.fn();
374
-
375
- middleware(req, res, next);
376
-
377
- expect(res.status).toHaveBeenCalledWith(401);
378
- expect(next).not.toHaveBeenCalled();
379
- });
380
- });
381
-
382
- describe('validateRequestBody', () => {
383
- it('validates required fields', () => {
384
- const middleware = validateRequestBody(['prompt']);
385
- const req = { body: {} };
386
- const res = {
387
- json: vi.fn(),
388
- status: vi.fn().mockReturnThis(),
389
- };
390
- const next = vi.fn();
391
-
392
- middleware(req, res, next);
393
-
394
- expect(res.status).toHaveBeenCalledWith(400);
395
- expect(next).not.toHaveBeenCalled();
396
- });
397
-
398
- it('passes valid body', () => {
399
- const middleware = validateRequestBody(['prompt']);
400
- const req = { body: { prompt: 'Test prompt' } };
401
- const res = {
402
- json: vi.fn(),
403
- status: vi.fn().mockReturnThis(),
404
- };
405
- const next = vi.fn();
406
-
407
- middleware(req, res, next);
408
-
409
- expect(next).toHaveBeenCalled();
410
- });
411
-
412
- it('validates multiple required fields', () => {
413
- const middleware = validateRequestBody(['prompt', 'capability']);
414
- const req = { body: { prompt: 'Test' } };
415
- const res = {
416
- json: vi.fn(),
417
- status: vi.fn().mockReturnThis(),
418
- };
419
- const next = vi.fn();
420
-
421
- middleware(req, res, next);
422
-
423
- expect(res.status).toHaveBeenCalledWith(400);
424
- });
425
- });
426
- });
1
+ import { describe, it, expect, vi } from 'vitest';
2
+ import {
3
+ handleRun,
4
+ handleTask,
5
+ handleReview,
6
+ handleDesign,
7
+ handleHealth,
8
+ } from './devserver-router-api.js';
9
+
10
+ describe('Devserver Router API', () => {
11
+ describe('POST /api/run', () => {
12
+ it('queues task', async () => {
13
+ const req = { body: { provider: 'claude', prompt: 'test' } };
14
+ const res = { json: vi.fn(), status: vi.fn().mockReturnThis() };
15
+
16
+ await handleRun(req, res, { queue: { enqueue: vi.fn().mockReturnValue('task-1') } });
17
+
18
+ expect(res.json).toHaveBeenCalledWith(expect.objectContaining({ taskId: 'task-1' }));
19
+ });
20
+
21
+ it('returns taskId', async () => {
22
+ const req = { body: { provider: 'claude', prompt: 'test' } };
23
+ const res = { json: vi.fn(), status: vi.fn().mockReturnThis() };
24
+
25
+ await handleRun(req, res, { queue: { enqueue: vi.fn().mockReturnValue('abc123') } });
26
+
27
+ expect(res.json).toHaveBeenCalledWith({ taskId: 'abc123' });
28
+ });
29
+ });
30
+
31
+ describe('GET /api/task/:taskId', () => {
32
+ it('returns pending status', async () => {
33
+ const req = { params: { taskId: 'task-1' } };
34
+ const res = { json: vi.fn() };
35
+
36
+ await handleTask(req, res, { queue: { getTask: vi.fn().mockReturnValue({ status: 'pending' }) } });
37
+
38
+ expect(res.json).toHaveBeenCalledWith(expect.objectContaining({ status: 'pending' }));
39
+ });
40
+
41
+ it('returns completed result', async () => {
42
+ const req = { params: { taskId: 'task-1' } };
43
+ const res = { json: vi.fn() };
44
+
45
+ await handleTask(req, res, {
46
+ queue: { getTask: vi.fn().mockReturnValue({ status: 'completed', result: { data: 'test' } }) }
47
+ });
48
+
49
+ expect(res.json).toHaveBeenCalledWith(expect.objectContaining({
50
+ status: 'completed',
51
+ result: { data: 'test' }
52
+ }));
53
+ });
54
+ });
55
+
56
+ describe('POST /api/review', () => {
57
+ it('runs multiple providers', async () => {
58
+ const req = { body: { code: 'test code' } };
59
+ const res = { json: vi.fn() };
60
+ const runProvider = vi.fn().mockResolvedValue({ parsed: { score: 80 } });
61
+
62
+ await handleReview(req, res, { runProvider, providers: ['claude', 'codex'] });
63
+
64
+ expect(runProvider).toHaveBeenCalledTimes(2);
65
+ });
66
+
67
+ it('returns consensus', async () => {
68
+ const req = { body: { code: 'test code' } };
69
+ const res = { json: vi.fn() };
70
+ const runProvider = vi.fn().mockResolvedValue({ parsed: { score: 80, approved: true } });
71
+
72
+ await handleReview(req, res, { runProvider, providers: ['claude'] });
73
+
74
+ expect(res.json).toHaveBeenCalledWith(expect.objectContaining({ consensus: expect.anything() }));
75
+ });
76
+ });
77
+
78
+ describe('POST /api/design', () => {
79
+ it('routes to gemini', async () => {
80
+ const req = { body: { description: 'design a button' } };
81
+ const res = { json: vi.fn() };
82
+ const runProvider = vi.fn().mockResolvedValue({ parsed: { mockups: [] } });
83
+
84
+ await handleDesign(req, res, { runProvider });
85
+
86
+ expect(runProvider).toHaveBeenCalledWith('gemini', expect.anything());
87
+ });
88
+ });
89
+
90
+ describe('GET /api/health', () => {
91
+ it('shows provider status', async () => {
92
+ const req = {};
93
+ const res = { json: vi.fn() };
94
+
95
+ await handleHealth(req, res, {
96
+ getProviderStatus: vi.fn().mockReturnValue({ claude: 'available', codex: 'unavailable' })
97
+ });
98
+
99
+ expect(res.json).toHaveBeenCalledWith(expect.objectContaining({
100
+ providers: expect.anything()
101
+ }));
102
+ });
103
+ });
104
+
105
+ describe('Authentication', () => {
106
+ it('rejects unauthenticated requests', async () => {
107
+ const req = { headers: {} };
108
+ const res = { json: vi.fn(), status: vi.fn().mockReturnThis() };
109
+
110
+ await handleRun(req, res, { requireAuth: true });
111
+
112
+ expect(res.status).toHaveBeenCalledWith(401);
113
+ });
114
+ });
115
+
116
+ describe('Validation', () => {
117
+ it('validates request body', async () => {
118
+ const req = { body: {} };
119
+ const res = { json: vi.fn(), status: vi.fn().mockReturnThis() };
120
+
121
+ await handleRun(req, res, { queue: { enqueue: vi.fn() } });
122
+
123
+ expect(res.status).toHaveBeenCalledWith(400);
124
+ });
125
+ });
126
+ });