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.
Files changed (72) 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 +4 -1
  44. package/server/dashboard/index.html +284 -13
  45. package/server/dashboard/login.html +262 -0
  46. package/server/index.js +304 -0
  47. package/server/lib/api-provider.js +104 -186
  48. package/server/lib/api-provider.test.js +238 -336
  49. package/server/lib/cli-detector.js +90 -166
  50. package/server/lib/cli-detector.test.js +114 -269
  51. package/server/lib/cli-provider.js +142 -212
  52. package/server/lib/cli-provider.test.js +196 -349
  53. package/server/lib/debug.test.js +3 -3
  54. package/server/lib/devserver-router-api.js +54 -249
  55. package/server/lib/devserver-router-api.test.js +126 -426
  56. package/server/lib/introspect.js +309 -0
  57. package/server/lib/introspect.test.js +286 -0
  58. package/server/lib/model-router.js +107 -245
  59. package/server/lib/model-router.test.js +122 -313
  60. package/server/lib/output-schemas.js +146 -269
  61. package/server/lib/output-schemas.test.js +106 -307
  62. package/server/lib/plan-parser.js +59 -16
  63. package/server/lib/provider-interface.js +99 -153
  64. package/server/lib/provider-interface.test.js +228 -394
  65. package/server/lib/provider-queue.js +164 -158
  66. package/server/lib/provider-queue.test.js +186 -315
  67. package/server/lib/router-config.js +99 -221
  68. package/server/lib/router-config.test.js +83 -237
  69. package/server/lib/router-setup-command.js +94 -419
  70. package/server/lib/router-setup-command.test.js +96 -375
  71. package/server/lib/router-status-api.js +93 -0
  72. package/server/lib/router-status-api.test.js +270 -0
@@ -1,315 +1,186 @@
1
- import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
- import {
3
- createQueue,
4
- enqueue,
5
- getStatus,
6
- clearQueue,
7
- drainQueue,
8
- PRIORITY,
9
- } from './provider-queue.js';
10
-
11
- describe('provider-queue', () => {
12
- let queue;
13
-
14
- beforeEach(() => {
15
- queue = createQueue({ maxConcurrent: 3 });
16
- });
17
-
18
- afterEach(() => {
19
- if (queue) {
20
- clearQueue(queue);
21
- }
22
- });
23
-
24
- describe('createQueue', () => {
25
- it('creates queue with default maxConcurrent', () => {
26
- const q = createQueue();
27
- expect(q.maxConcurrent).toBe(3);
28
- });
29
-
30
- it('creates queue with custom maxConcurrent', () => {
31
- const q = createQueue({ maxConcurrent: 5 });
32
- expect(q.maxConcurrent).toBe(5);
33
- });
34
-
35
- it('creates queue with default timeout', () => {
36
- const q = createQueue();
37
- expect(q.timeout).toBe(120000);
38
- });
39
-
40
- it('creates queue with custom timeout', () => {
41
- const q = createQueue({ timeout: 60000 });
42
- expect(q.timeout).toBe(60000);
43
- });
44
- });
45
-
46
- describe('enqueue', () => {
47
- it('adds task to queue', async () => {
48
- const task = { id: 'task-1', execute: vi.fn().mockResolvedValue('result') };
49
-
50
- const promise = enqueue(queue, task);
51
-
52
- expect(getStatus(queue).pending).toBeGreaterThanOrEqual(0);
53
- await promise;
54
- });
55
-
56
- it('returns promise that resolves with result', async () => {
57
- const task = {
58
- id: 'task-1',
59
- execute: vi.fn().mockResolvedValue('my-result'),
60
- };
61
-
62
- const result = await enqueue(queue, task);
63
-
64
- expect(result).toBe('my-result');
65
- });
66
-
67
- it('executes task function', async () => {
68
- const executeFn = vi.fn().mockResolvedValue('done');
69
- const task = { id: 'task-1', execute: executeFn };
70
-
71
- await enqueue(queue, task);
72
-
73
- expect(executeFn).toHaveBeenCalled();
74
- });
75
- });
76
-
77
- describe('dequeue respects maxConcurrent', () => {
78
- it('limits concurrent executions', async () => {
79
- const q = createQueue({ maxConcurrent: 2 });
80
- let concurrentCount = 0;
81
- let maxConcurrent = 0;
82
-
83
- const createSlowTask = (id) => ({
84
- id,
85
- execute: async () => {
86
- concurrentCount++;
87
- maxConcurrent = Math.max(maxConcurrent, concurrentCount);
88
- await new Promise(r => setTimeout(r, 50));
89
- concurrentCount--;
90
- return id;
91
- },
92
- });
93
-
94
- const promises = [
95
- enqueue(q, createSlowTask('t1')),
96
- enqueue(q, createSlowTask('t2')),
97
- enqueue(q, createSlowTask('t3')),
98
- enqueue(q, createSlowTask('t4')),
99
- ];
100
-
101
- await Promise.all(promises);
102
-
103
- expect(maxConcurrent).toBeLessThanOrEqual(2);
104
- });
105
- });
106
-
107
- describe('dequeue processes FIFO', () => {
108
- it('processes tasks in order', async () => {
109
- const q = createQueue({ maxConcurrent: 1 });
110
- const order = [];
111
-
112
- const createTask = (id) => ({
113
- id,
114
- execute: async () => {
115
- order.push(id);
116
- return id;
117
- },
118
- });
119
-
120
- await Promise.all([
121
- enqueue(q, createTask('first')),
122
- enqueue(q, createTask('second')),
123
- enqueue(q, createTask('third')),
124
- ]);
125
-
126
- expect(order).toEqual(['first', 'second', 'third']);
127
- });
128
- });
129
-
130
- describe('timeout handling', () => {
131
- it('cancels slow tasks', async () => {
132
- const q = createQueue({ maxConcurrent: 1, timeout: 50 });
133
-
134
- const slowTask = {
135
- id: 'slow',
136
- execute: () => new Promise(r => setTimeout(r, 200)),
137
- };
138
-
139
- await expect(enqueue(q, slowTask)).rejects.toThrow(/timeout/i);
140
- });
141
- });
142
-
143
- describe('getStatus', () => {
144
- it('returns queue length', () => {
145
- const status = getStatus(queue);
146
- expect(typeof status.pending).toBe('number');
147
- });
148
-
149
- it('returns running count', () => {
150
- const status = getStatus(queue);
151
- expect(typeof status.running).toBe('number');
152
- });
153
-
154
- it('returns completed count', () => {
155
- const status = getStatus(queue);
156
- expect(typeof status.completed).toBe('number');
157
- });
158
-
159
- it('updates after task completion', async () => {
160
- const task = { id: 't1', execute: vi.fn().mockResolvedValue('done') };
161
-
162
- await enqueue(queue, task);
163
- const status = getStatus(queue);
164
-
165
- expect(status.completed).toBeGreaterThanOrEqual(1);
166
- });
167
- });
168
-
169
- describe('priority levels', () => {
170
- it('urgent goes first', async () => {
171
- const q = createQueue({ maxConcurrent: 1 });
172
- const order = [];
173
-
174
- // Pause processing
175
- let resolver;
176
- const blocker = {
177
- id: 'blocker',
178
- execute: () => new Promise(r => { resolver = r; }),
179
- };
180
- const blockerPromise = enqueue(q, blocker);
181
-
182
- // Queue tasks with different priorities
183
- const normalPromise = enqueue(q, {
184
- id: 'normal',
185
- priority: PRIORITY.NORMAL,
186
- execute: async () => { order.push('normal'); return 'normal'; },
187
- });
188
-
189
- const urgentPromise = enqueue(q, {
190
- id: 'urgent',
191
- priority: PRIORITY.URGENT,
192
- execute: async () => { order.push('urgent'); return 'urgent'; },
193
- });
194
-
195
- // Release blocker
196
- resolver('done');
197
- await blockerPromise;
198
- await Promise.all([normalPromise, urgentPromise]);
199
-
200
- // Urgent should have been processed before normal
201
- expect(order[0]).toBe('urgent');
202
- });
203
-
204
- it('low priority goes last', async () => {
205
- const q = createQueue({ maxConcurrent: 1 });
206
- const order = [];
207
-
208
- let resolver;
209
- const blocker = {
210
- id: 'blocker',
211
- execute: () => new Promise(r => { resolver = r; }),
212
- };
213
- const blockerPromise = enqueue(q, blocker);
214
-
215
- const lowPromise = enqueue(q, {
216
- id: 'low',
217
- priority: PRIORITY.LOW,
218
- execute: async () => { order.push('low'); return 'low'; },
219
- });
220
-
221
- const normalPromise = enqueue(q, {
222
- id: 'normal',
223
- priority: PRIORITY.NORMAL,
224
- execute: async () => { order.push('normal'); return 'normal'; },
225
- });
226
-
227
- resolver('done');
228
- await blockerPromise;
229
- await Promise.all([lowPromise, normalPromise]);
230
-
231
- expect(order[order.length - 1]).toBe('low');
232
- });
233
- });
234
-
235
- describe('clearQueue', () => {
236
- it('removes all pending tasks', async () => {
237
- const q = createQueue({ maxConcurrent: 1 });
238
-
239
- // Block the queue
240
- let resolver;
241
- enqueue(q, {
242
- id: 'blocker',
243
- execute: () => new Promise(r => { resolver = r; }),
244
- });
245
-
246
- // Add pending tasks
247
- const pending1 = enqueue(q, { id: 'p1', execute: vi.fn() });
248
- const pending2 = enqueue(q, { id: 'p2', execute: vi.fn() });
249
-
250
- clearQueue(q);
251
- resolver('done');
252
-
253
- // Pending tasks should be rejected
254
- await expect(pending1).rejects.toThrow(/cleared/i);
255
- await expect(pending2).rejects.toThrow(/cleared/i);
256
- });
257
- });
258
-
259
- describe('drainQueue', () => {
260
- it('waits for all tasks to complete', async () => {
261
- const q = createQueue({ maxConcurrent: 2 });
262
- const completed = [];
263
-
264
- enqueue(q, {
265
- id: 't1',
266
- execute: async () => {
267
- await new Promise(r => setTimeout(r, 30));
268
- completed.push('t1');
269
- },
270
- });
271
-
272
- enqueue(q, {
273
- id: 't2',
274
- execute: async () => {
275
- await new Promise(r => setTimeout(r, 20));
276
- completed.push('t2');
277
- },
278
- });
279
-
280
- await drainQueue(q);
281
-
282
- expect(completed).toContain('t1');
283
- expect(completed).toContain('t2');
284
- });
285
-
286
- it('resolves immediately if queue is empty', async () => {
287
- const q = createQueue();
288
-
289
- const start = Date.now();
290
- await drainQueue(q);
291
- const elapsed = Date.now() - start;
292
-
293
- expect(elapsed).toBeLessThan(50);
294
- });
295
- });
296
-
297
- describe('PRIORITY constants', () => {
298
- it('exports URGENT', () => {
299
- expect(PRIORITY.URGENT).toBeDefined();
300
- });
301
-
302
- it('exports NORMAL', () => {
303
- expect(PRIORITY.NORMAL).toBeDefined();
304
- });
305
-
306
- it('exports LOW', () => {
307
- expect(PRIORITY.LOW).toBeDefined();
308
- });
309
-
310
- it('URGENT > NORMAL > LOW', () => {
311
- expect(PRIORITY.URGENT).toBeGreaterThan(PRIORITY.NORMAL);
312
- expect(PRIORITY.NORMAL).toBeGreaterThan(PRIORITY.LOW);
313
- });
314
- });
315
- });
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+ import {
3
+ ProviderQueue,
4
+ Priority,
5
+ } from './provider-queue.js';
6
+
7
+ describe('Provider Queue', () => {
8
+ let queue;
9
+
10
+ beforeEach(() => {
11
+ queue = new ProviderQueue({ maxConcurrent: 3 });
12
+ });
13
+
14
+ describe('enqueue', () => {
15
+ it('adds task to queue', () => {
16
+ const task = { id: '1', prompt: 'test' };
17
+ queue.enqueue(task);
18
+
19
+ expect(queue.getStatus().pending).toBe(1);
20
+ });
21
+
22
+ it('returns task id', () => {
23
+ const taskId = queue.enqueue({ prompt: 'test' });
24
+ expect(taskId).toBeDefined();
25
+ });
26
+ });
27
+
28
+ describe('dequeue', () => {
29
+ it('respects maxConcurrent', async () => {
30
+ const tasks = [];
31
+ for (let i = 0; i < 5; i++) {
32
+ tasks.push(queue.enqueue({
33
+ prompt: `test ${i}`,
34
+ execute: () => new Promise(r => setTimeout(r, 100)),
35
+ }));
36
+ }
37
+
38
+ // Start processing
39
+ queue.process();
40
+
41
+ // Should have max 3 running
42
+ expect(queue.getStatus().running).toBeLessThanOrEqual(3);
43
+ });
44
+
45
+ it('processes FIFO', async () => {
46
+ const order = [];
47
+
48
+ queue.enqueue({
49
+ id: 'first',
50
+ execute: async () => { order.push('first'); },
51
+ });
52
+ queue.enqueue({
53
+ id: 'second',
54
+ execute: async () => { order.push('second'); },
55
+ });
56
+ queue.enqueue({
57
+ id: 'third',
58
+ execute: async () => { order.push('third'); },
59
+ });
60
+
61
+ await queue.drainQueue();
62
+
63
+ expect(order).toEqual(['first', 'second', 'third']);
64
+ });
65
+ });
66
+
67
+ describe('timeout', () => {
68
+ it('cancels slow tasks', async () => {
69
+ const slowQueue = new ProviderQueue({ maxConcurrent: 1, timeout: 50 });
70
+
71
+ const taskId = slowQueue.enqueue({
72
+ execute: () => new Promise(r => setTimeout(r, 1000)),
73
+ });
74
+
75
+ slowQueue.process();
76
+ await new Promise(r => setTimeout(r, 100));
77
+
78
+ const status = slowQueue.getTaskStatus(taskId);
79
+ expect(status).toBe('cancelled');
80
+ });
81
+ });
82
+
83
+ describe('getStatus', () => {
84
+ it('returns queue length', () => {
85
+ queue.enqueue({ prompt: 'test 1' });
86
+ queue.enqueue({ prompt: 'test 2' });
87
+
88
+ const status = queue.getStatus();
89
+ expect(status.pending).toBe(2);
90
+ });
91
+
92
+ it('returns running count', async () => {
93
+ queue.enqueue({
94
+ execute: () => new Promise(r => setTimeout(r, 100)),
95
+ });
96
+
97
+ queue.process();
98
+
99
+ const status = queue.getStatus();
100
+ expect(status.running).toBe(1);
101
+ });
102
+ });
103
+
104
+ describe('priority', () => {
105
+ it('urgent goes first', async () => {
106
+ const order = [];
107
+
108
+ queue.enqueue({
109
+ priority: Priority.LOW,
110
+ execute: async () => { order.push('low'); },
111
+ });
112
+ queue.enqueue({
113
+ priority: Priority.URGENT,
114
+ execute: async () => { order.push('urgent'); },
115
+ });
116
+ queue.enqueue({
117
+ priority: Priority.NORMAL,
118
+ execute: async () => { order.push('normal'); },
119
+ });
120
+
121
+ await queue.drainQueue();
122
+
123
+ expect(order[0]).toBe('urgent');
124
+ });
125
+
126
+ it('affects ordering', async () => {
127
+ const order = [];
128
+
129
+ queue.enqueue({
130
+ priority: Priority.LOW,
131
+ execute: async () => { order.push('low'); },
132
+ });
133
+ queue.enqueue({
134
+ priority: Priority.NORMAL,
135
+ execute: async () => { order.push('normal'); },
136
+ });
137
+
138
+ await queue.drainQueue();
139
+
140
+ expect(order.indexOf('normal')).toBeLessThan(order.indexOf('low'));
141
+ });
142
+ });
143
+
144
+ describe('clearQueue', () => {
145
+ it('removes all pending', () => {
146
+ queue.enqueue({ prompt: 'test 1' });
147
+ queue.enqueue({ prompt: 'test 2' });
148
+ queue.enqueue({ prompt: 'test 3' });
149
+
150
+ queue.clearQueue();
151
+
152
+ expect(queue.getStatus().pending).toBe(0);
153
+ });
154
+ });
155
+
156
+ describe('drainQueue', () => {
157
+ it('waits for completion', async () => {
158
+ let completed = false;
159
+
160
+ queue.enqueue({
161
+ execute: async () => {
162
+ await new Promise(r => setTimeout(r, 50));
163
+ completed = true;
164
+ },
165
+ });
166
+
167
+ await queue.drainQueue();
168
+
169
+ expect(completed).toBe(true);
170
+ });
171
+ });
172
+ });
173
+
174
+ describe('Priority', () => {
175
+ it('exports URGENT constant', () => {
176
+ expect(Priority.URGENT).toBe('urgent');
177
+ });
178
+
179
+ it('exports NORMAL constant', () => {
180
+ expect(Priority.NORMAL).toBe('normal');
181
+ });
182
+
183
+ it('exports LOW constant', () => {
184
+ expect(Priority.LOW).toBe('low');
185
+ });
186
+ });