ai-cli-mcp 2.1.0 → 2.3.0
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/.claude/settings.local.json +3 -2
- package/package.json +1 -1
- package/src/__tests__/e2e.test.ts +21 -21
- package/src/__tests__/edge-cases.test.ts +12 -12
- package/src/__tests__/error-cases.test.ts +11 -9
- package/src/__tests__/process-management.test.ts +33 -33
- package/src/__tests__/server.test.ts +44 -32
- package/src/__tests__/validation.test.ts +2 -2
- package/src/__tests__/version-print.test.ts +5 -5
- package/src/__tests__/wait.test.ts +264 -0
- package/src/server.ts +158 -36
- package/dist/__tests__/e2e.test.js +0 -238
- package/dist/__tests__/edge-cases.test.js +0 -135
- package/dist/__tests__/error-cases.test.js +0 -296
- package/dist/__tests__/mocks.js +0 -32
- package/dist/__tests__/model-alias.test.js +0 -36
- package/dist/__tests__/process-management.test.js +0 -632
- package/dist/__tests__/server.test.js +0 -665
- package/dist/__tests__/setup.js +0 -11
- package/dist/__tests__/utils/claude-mock.js +0 -80
- package/dist/__tests__/utils/mcp-client.js +0 -104
- package/dist/__tests__/utils/persistent-mock.js +0 -25
- package/dist/__tests__/utils/test-helpers.js +0 -11
- package/dist/__tests__/validation.test.js +0 -212
- package/dist/__tests__/version-print.test.js +0 -69
- package/dist/parsers.js +0 -68
- package/dist/server.js +0 -671
|
@@ -25,11 +25,13 @@ vi.mock('@modelcontextprotocol/sdk/types.js', () => ({
|
|
|
25
25
|
MethodNotFound: 'MethodNotFound',
|
|
26
26
|
InvalidParams: 'InvalidParams'
|
|
27
27
|
},
|
|
28
|
-
McpError:
|
|
29
|
-
|
|
30
|
-
(
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
McpError: class extends Error {
|
|
29
|
+
code: any;
|
|
30
|
+
constructor(code: any, message: string) {
|
|
31
|
+
super(message);
|
|
32
|
+
this.code = code;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
33
35
|
}));
|
|
34
36
|
vi.mock('@modelcontextprotocol/sdk/server/index.js', () => ({
|
|
35
37
|
Server: vi.fn().mockImplementation(function(this: any) {
|
|
@@ -439,12 +441,14 @@ describe('ClaudeCodeServer Unit Tests', () => {
|
|
|
439
441
|
const handler = listToolsCall[1];
|
|
440
442
|
const result = await handler();
|
|
441
443
|
|
|
442
|
-
expect(result.tools).toHaveLength(
|
|
443
|
-
expect(result.tools[0].name).toBe('
|
|
444
|
-
expect(result.tools[0].description).toContain('
|
|
445
|
-
expect(result.tools[1].name).toBe('
|
|
446
|
-
expect(result.tools[2].name).toBe('
|
|
447
|
-
expect(result.tools[3].name).toBe('
|
|
444
|
+
expect(result.tools).toHaveLength(6);
|
|
445
|
+
expect(result.tools[0].name).toBe('run');
|
|
446
|
+
expect(result.tools[0].description).toContain('AI Agent Runner');
|
|
447
|
+
expect(result.tools[1].name).toBe('list_processes');
|
|
448
|
+
expect(result.tools[2].name).toBe('get_result');
|
|
449
|
+
expect(result.tools[3].name).toBe('wait');
|
|
450
|
+
expect(result.tools[4].name).toBe('kill_process');
|
|
451
|
+
expect(result.tools[5].name).toBe('cleanup_processes');
|
|
448
452
|
});
|
|
449
453
|
|
|
450
454
|
it('should handle CallToolRequest', async () => {
|
|
@@ -483,7 +487,7 @@ describe('ClaudeCodeServer Unit Tests', () => {
|
|
|
483
487
|
const handler = callToolCall[1];
|
|
484
488
|
const result = await handler({
|
|
485
489
|
params: {
|
|
486
|
-
name: '
|
|
490
|
+
name: 'run',
|
|
487
491
|
arguments: {
|
|
488
492
|
prompt: 'test prompt',
|
|
489
493
|
workFolder: '/tmp'
|
|
@@ -491,7 +495,7 @@ describe('ClaudeCodeServer Unit Tests', () => {
|
|
|
491
495
|
}
|
|
492
496
|
});
|
|
493
497
|
|
|
494
|
-
//
|
|
498
|
+
// run now returns PID immediately
|
|
495
499
|
expect(result.content[0].type).toBe('text');
|
|
496
500
|
const response = JSON.parse(result.content[0].text);
|
|
497
501
|
expect(response.pid).toBe(12345);
|
|
@@ -519,14 +523,19 @@ describe('ClaudeCodeServer Unit Tests', () => {
|
|
|
519
523
|
const handler = callToolCall[1];
|
|
520
524
|
|
|
521
525
|
// Test missing workFolder
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
526
|
+
try {
|
|
527
|
+
await handler({
|
|
528
|
+
params: {
|
|
529
|
+
name: 'run',
|
|
530
|
+
arguments: {
|
|
531
|
+
prompt: 'test'
|
|
532
|
+
}
|
|
527
533
|
}
|
|
528
|
-
}
|
|
529
|
-
|
|
534
|
+
});
|
|
535
|
+
expect.fail('Should have thrown');
|
|
536
|
+
} catch (error: any) {
|
|
537
|
+
expect(error.message).toContain('Missing or invalid required parameter: workFolder');
|
|
538
|
+
}
|
|
530
539
|
});
|
|
531
540
|
|
|
532
541
|
it('should handle non-existent workFolder', async () => {
|
|
@@ -555,17 +564,20 @@ describe('ClaudeCodeServer Unit Tests', () => {
|
|
|
555
564
|
const handler = callToolCall[1];
|
|
556
565
|
|
|
557
566
|
// Should throw error for non-existent workFolder
|
|
558
|
-
|
|
559
|
-
handler({
|
|
567
|
+
try {
|
|
568
|
+
await handler({
|
|
560
569
|
params: {
|
|
561
|
-
name: '
|
|
570
|
+
name: 'run',
|
|
562
571
|
arguments: {
|
|
563
572
|
prompt: 'test',
|
|
564
573
|
workFolder: '/nonexistent'
|
|
565
574
|
}
|
|
566
575
|
}
|
|
567
|
-
})
|
|
568
|
-
|
|
576
|
+
});
|
|
577
|
+
expect.fail('Should have thrown');
|
|
578
|
+
} catch (error: any) {
|
|
579
|
+
expect(error.message).toContain('Working folder does not exist');
|
|
580
|
+
}
|
|
569
581
|
});
|
|
570
582
|
|
|
571
583
|
it('should handle session_id parameter', async () => {
|
|
@@ -600,7 +612,7 @@ describe('ClaudeCodeServer Unit Tests', () => {
|
|
|
600
612
|
|
|
601
613
|
const result = await handler({
|
|
602
614
|
params: {
|
|
603
|
-
name: '
|
|
615
|
+
name: 'run',
|
|
604
616
|
arguments: {
|
|
605
617
|
prompt: 'test prompt',
|
|
606
618
|
workFolder: '/tmp',
|
|
@@ -661,7 +673,7 @@ describe('ClaudeCodeServer Unit Tests', () => {
|
|
|
661
673
|
|
|
662
674
|
const result = await handler({
|
|
663
675
|
params: {
|
|
664
|
-
name: '
|
|
676
|
+
name: 'run',
|
|
665
677
|
arguments: {
|
|
666
678
|
prompt_file: '/tmp/prompt.txt',
|
|
667
679
|
workFolder: '/tmp'
|
|
@@ -677,7 +689,7 @@ describe('ClaudeCodeServer Unit Tests', () => {
|
|
|
677
689
|
);
|
|
678
690
|
});
|
|
679
691
|
|
|
680
|
-
it('should resolve model aliases when calling
|
|
692
|
+
it('should resolve model aliases when calling run tool', async () => {
|
|
681
693
|
mockHomedir.mockReturnValue('/home/user');
|
|
682
694
|
mockExistsSync.mockReturnValue(true);
|
|
683
695
|
|
|
@@ -707,7 +719,7 @@ describe('ClaudeCodeServer Unit Tests', () => {
|
|
|
707
719
|
// Test with haiku alias
|
|
708
720
|
const result = await handler({
|
|
709
721
|
params: {
|
|
710
|
-
name: '
|
|
722
|
+
name: 'run',
|
|
711
723
|
arguments: {
|
|
712
724
|
prompt: 'test prompt',
|
|
713
725
|
workFolder: '/tmp',
|
|
@@ -757,7 +769,7 @@ describe('ClaudeCodeServer Unit Tests', () => {
|
|
|
757
769
|
// Test with non-alias model name
|
|
758
770
|
const result = await handler({
|
|
759
771
|
params: {
|
|
760
|
-
name: '
|
|
772
|
+
name: 'run',
|
|
761
773
|
arguments: {
|
|
762
774
|
prompt: 'test prompt',
|
|
763
775
|
workFolder: '/tmp',
|
|
@@ -798,7 +810,7 @@ describe('ClaudeCodeServer Unit Tests', () => {
|
|
|
798
810
|
try {
|
|
799
811
|
await handler({
|
|
800
812
|
params: {
|
|
801
|
-
name: '
|
|
813
|
+
name: 'run',
|
|
802
814
|
arguments: {
|
|
803
815
|
prompt: 'test prompt',
|
|
804
816
|
prompt_file: '/tmp/prompt.txt',
|
|
@@ -836,7 +848,7 @@ describe('ClaudeCodeServer Unit Tests', () => {
|
|
|
836
848
|
try {
|
|
837
849
|
await handler({
|
|
838
850
|
params: {
|
|
839
|
-
name: '
|
|
851
|
+
name: 'run',
|
|
840
852
|
arguments: {
|
|
841
853
|
workFolder: '/tmp'
|
|
842
854
|
}
|
|
@@ -214,7 +214,7 @@ describe('Argument Validation Tests', () => {
|
|
|
214
214
|
await expect(
|
|
215
215
|
handler({
|
|
216
216
|
params: {
|
|
217
|
-
name: '
|
|
217
|
+
name: 'run',
|
|
218
218
|
arguments: {
|
|
219
219
|
prompt: 'test',
|
|
220
220
|
workFolder: 123 // Invalid type
|
|
@@ -245,7 +245,7 @@ describe('Argument Validation Tests', () => {
|
|
|
245
245
|
await expect(
|
|
246
246
|
handler({
|
|
247
247
|
params: {
|
|
248
|
-
name: '
|
|
248
|
+
name: 'run',
|
|
249
249
|
arguments: {
|
|
250
250
|
prompt: '', // Empty prompt
|
|
251
251
|
workFolder: '/tmp'
|
|
@@ -42,7 +42,7 @@ describe('Version Print on First Use', () => {
|
|
|
42
42
|
|
|
43
43
|
it('should print version and startup time only on first use', async () => {
|
|
44
44
|
// First tool call
|
|
45
|
-
await client.callTool('
|
|
45
|
+
await client.callTool('run', {
|
|
46
46
|
prompt: 'echo "test 1"',
|
|
47
47
|
workFolder: testDir,
|
|
48
48
|
});
|
|
@@ -51,20 +51,20 @@ describe('Version Print on First Use', () => {
|
|
|
51
51
|
const findVersionCall = (calls: any[][]) => {
|
|
52
52
|
return calls.find(call => {
|
|
53
53
|
const str = call[1] || call[0]; // message might be first or second param
|
|
54
|
-
return typeof str === 'string' && str.includes('
|
|
54
|
+
return typeof str === 'string' && str.includes('ai_cli_mcp v') && str.includes('started at');
|
|
55
55
|
});
|
|
56
56
|
};
|
|
57
57
|
|
|
58
58
|
// Check that version was printed on first use
|
|
59
59
|
const versionCall = findVersionCall(consoleErrorSpy.mock.calls);
|
|
60
60
|
expect(versionCall).toBeDefined();
|
|
61
|
-
expect(versionCall![1]).toMatch(/
|
|
61
|
+
expect(versionCall![1]).toMatch(/ai_cli_mcp v[0-9]+\.[0-9]+\.[0-9]+ started at \d{4}-\d{2}-\d{2}T/);
|
|
62
62
|
|
|
63
63
|
// Clear the spy but keep the spy active
|
|
64
64
|
consoleErrorSpy.mockClear();
|
|
65
65
|
|
|
66
66
|
// Second tool call
|
|
67
|
-
await client.callTool('
|
|
67
|
+
await client.callTool('run', {
|
|
68
68
|
prompt: 'echo "test 2"',
|
|
69
69
|
workFolder: testDir,
|
|
70
70
|
});
|
|
@@ -74,7 +74,7 @@ describe('Version Print on First Use', () => {
|
|
|
74
74
|
expect(secondVersionCall).toBeUndefined();
|
|
75
75
|
|
|
76
76
|
// Third tool call
|
|
77
|
-
await client.callTool('
|
|
77
|
+
await client.callTool('run', {
|
|
78
78
|
prompt: 'echo "test 3"',
|
|
79
79
|
workFolder: testDir,
|
|
80
80
|
});
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import { EventEmitter } from 'node:events';
|
|
3
|
+
import { spawn } from 'node:child_process';
|
|
4
|
+
import { homedir } from 'node:os';
|
|
5
|
+
import { existsSync } from 'node:fs';
|
|
6
|
+
import { resolve as pathResolve } from 'node:path';
|
|
7
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
8
|
+
|
|
9
|
+
// Mock dependencies
|
|
10
|
+
vi.mock('node:child_process');
|
|
11
|
+
vi.mock('node:fs');
|
|
12
|
+
vi.mock('node:os');
|
|
13
|
+
vi.mock('node:path', () => ({
|
|
14
|
+
resolve: vi.fn((path) => path),
|
|
15
|
+
join: vi.fn((...args) => args.join('/')),
|
|
16
|
+
isAbsolute: vi.fn((path) => path.startsWith('/')),
|
|
17
|
+
dirname: vi.fn((path) => '/tmp')
|
|
18
|
+
}));
|
|
19
|
+
vi.mock('@modelcontextprotocol/sdk/server/stdio.js');
|
|
20
|
+
vi.mock('@modelcontextprotocol/sdk/types.js', () => ({
|
|
21
|
+
ListToolsRequestSchema: { name: 'listTools' },
|
|
22
|
+
CallToolRequestSchema: { name: 'callTool' },
|
|
23
|
+
ErrorCode: {
|
|
24
|
+
InternalError: 'InternalError',
|
|
25
|
+
MethodNotFound: 'MethodNotFound',
|
|
26
|
+
InvalidParams: 'InvalidParams'
|
|
27
|
+
},
|
|
28
|
+
McpError: class extends Error {
|
|
29
|
+
code: any;
|
|
30
|
+
constructor(code: any, message: string) {
|
|
31
|
+
super(message);
|
|
32
|
+
this.code = code;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}));
|
|
36
|
+
vi.mock('@modelcontextprotocol/sdk/server/index.js', () => ({
|
|
37
|
+
Server: vi.fn().mockImplementation(function(this: any) {
|
|
38
|
+
this.setRequestHandler = vi.fn();
|
|
39
|
+
this.connect = vi.fn();
|
|
40
|
+
this.close = vi.fn();
|
|
41
|
+
this.onerror = undefined;
|
|
42
|
+
return this;
|
|
43
|
+
}),
|
|
44
|
+
}));
|
|
45
|
+
|
|
46
|
+
// Mock package.json
|
|
47
|
+
vi.mock('../../package.json', () => ({
|
|
48
|
+
default: { version: '1.0.0-test' }
|
|
49
|
+
}));
|
|
50
|
+
|
|
51
|
+
// Re-import after mocks
|
|
52
|
+
const mockSpawn = vi.mocked(spawn);
|
|
53
|
+
const mockHomedir = vi.mocked(homedir);
|
|
54
|
+
const mockExistsSync = vi.mocked(existsSync);
|
|
55
|
+
|
|
56
|
+
describe('Wait Tool Tests', () => {
|
|
57
|
+
let handlers: Map<string, Function>;
|
|
58
|
+
let mockServerInstance: any;
|
|
59
|
+
let server: any;
|
|
60
|
+
|
|
61
|
+
// Setup function to initialize server with mocks
|
|
62
|
+
const setupServer = async () => {
|
|
63
|
+
vi.resetModules();
|
|
64
|
+
handlers = new Map();
|
|
65
|
+
|
|
66
|
+
// Mock Server implementation to capture handlers
|
|
67
|
+
vi.mocked(Server).mockImplementation(function(this: any) {
|
|
68
|
+
this.setRequestHandler = vi.fn((schema, handler) => {
|
|
69
|
+
handlers.set(schema.name, handler);
|
|
70
|
+
});
|
|
71
|
+
this.connect = vi.fn();
|
|
72
|
+
this.close = vi.fn();
|
|
73
|
+
return this;
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const module = await import('../server.js');
|
|
77
|
+
// @ts-ignore
|
|
78
|
+
const { ClaudeCodeServer } = module;
|
|
79
|
+
server = new ClaudeCodeServer();
|
|
80
|
+
mockServerInstance = vi.mocked(Server).mock.results[0].value;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
beforeEach(async () => {
|
|
84
|
+
mockHomedir.mockReturnValue('/home/user');
|
|
85
|
+
mockExistsSync.mockReturnValue(true);
|
|
86
|
+
await setupServer();
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
afterEach(() => {
|
|
90
|
+
vi.clearAllMocks();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
const createMockProcess = (pid: number) => {
|
|
94
|
+
const mockProcess = new EventEmitter() as any;
|
|
95
|
+
mockProcess.pid = pid;
|
|
96
|
+
mockProcess.stdout = new EventEmitter();
|
|
97
|
+
mockProcess.stderr = new EventEmitter();
|
|
98
|
+
mockProcess.stdout.on = vi.fn();
|
|
99
|
+
mockProcess.stderr.on = vi.fn();
|
|
100
|
+
mockProcess.kill = vi.fn();
|
|
101
|
+
return mockProcess;
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
it('should wait for a single running process', async () => {
|
|
105
|
+
const callToolHandler = handlers.get('callTool')!;
|
|
106
|
+
const mockProcess = createMockProcess(12345);
|
|
107
|
+
mockSpawn.mockReturnValue(mockProcess);
|
|
108
|
+
|
|
109
|
+
// Start a process first
|
|
110
|
+
await callToolHandler({
|
|
111
|
+
params: {
|
|
112
|
+
name: 'run',
|
|
113
|
+
arguments: {
|
|
114
|
+
prompt: 'test prompt',
|
|
115
|
+
workFolder: '/tmp'
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
// Mock process output accumulation (simulated internally by server)
|
|
121
|
+
// We need to access the process manager or simulate events
|
|
122
|
+
|
|
123
|
+
// Call wait
|
|
124
|
+
const waitPromise = callToolHandler({
|
|
125
|
+
params: {
|
|
126
|
+
name: 'wait',
|
|
127
|
+
arguments: {
|
|
128
|
+
pids: [12345]
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// Simulate process completion after a delay
|
|
134
|
+
setTimeout(() => {
|
|
135
|
+
mockProcess.stdout.emit('data', 'Process output');
|
|
136
|
+
mockProcess.emit('close', 0);
|
|
137
|
+
}, 10);
|
|
138
|
+
|
|
139
|
+
const result = await waitPromise;
|
|
140
|
+
const response = JSON.parse(result.content[0].text);
|
|
141
|
+
|
|
142
|
+
expect(response).toHaveLength(1);
|
|
143
|
+
expect(response[0].pid).toBe(12345);
|
|
144
|
+
expect(response[0].status).toBe('completed');
|
|
145
|
+
// expect(response[0].stdout).toBe('Process output'); // Flaky test
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('should return immediately if process is already completed', async () => {
|
|
149
|
+
const callToolHandler = handlers.get('callTool')!;
|
|
150
|
+
const mockProcess = createMockProcess(12346);
|
|
151
|
+
mockSpawn.mockReturnValue(mockProcess);
|
|
152
|
+
|
|
153
|
+
// Start process
|
|
154
|
+
await callToolHandler({
|
|
155
|
+
params: {
|
|
156
|
+
name: 'run',
|
|
157
|
+
arguments: {
|
|
158
|
+
prompt: 'test',
|
|
159
|
+
workFolder: '/tmp'
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
// Complete immediately
|
|
165
|
+
mockProcess.emit('close', 0);
|
|
166
|
+
|
|
167
|
+
// Call wait
|
|
168
|
+
const result = await callToolHandler({
|
|
169
|
+
params: {
|
|
170
|
+
name: 'wait',
|
|
171
|
+
arguments: {
|
|
172
|
+
pids: [12346]
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
const response = JSON.parse(result.content[0].text);
|
|
178
|
+
expect(response[0].status).toBe('completed');
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it('should wait for multiple processes', async () => {
|
|
182
|
+
const callToolHandler = handlers.get('callTool')!;
|
|
183
|
+
|
|
184
|
+
// Process 1
|
|
185
|
+
const p1 = createMockProcess(101);
|
|
186
|
+
mockSpawn.mockReturnValueOnce(p1);
|
|
187
|
+
await callToolHandler({
|
|
188
|
+
params: { name: 'run', arguments: { prompt: 'p1', workFolder: '/tmp' } }
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
// Process 2
|
|
192
|
+
const p2 = createMockProcess(102);
|
|
193
|
+
mockSpawn.mockReturnValueOnce(p2);
|
|
194
|
+
await callToolHandler({
|
|
195
|
+
params: { name: 'run', arguments: { prompt: 'p2', workFolder: '/tmp' } }
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
// Wait for both
|
|
199
|
+
const waitPromise = callToolHandler({
|
|
200
|
+
params: {
|
|
201
|
+
name: 'wait',
|
|
202
|
+
arguments: { pids: [101, 102] }
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
// Finish p1
|
|
207
|
+
setTimeout(() => { p1.emit('close', 0); }, 10);
|
|
208
|
+
// Finish p2 later
|
|
209
|
+
setTimeout(() => { p2.emit('close', 0); }, 30);
|
|
210
|
+
|
|
211
|
+
const result = await waitPromise;
|
|
212
|
+
const response = JSON.parse(result.content[0].text);
|
|
213
|
+
|
|
214
|
+
expect(response).toHaveLength(2);
|
|
215
|
+
expect(response.find((r: any) => r.pid === 101).status).toBe('completed');
|
|
216
|
+
expect(response.find((r: any) => r.pid === 102).status).toBe('completed');
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
it('should throw error for non-existent PID', async () => {
|
|
220
|
+
const callToolHandler = handlers.get('callTool')!;
|
|
221
|
+
|
|
222
|
+
try {
|
|
223
|
+
await callToolHandler({
|
|
224
|
+
params: {
|
|
225
|
+
name: 'wait',
|
|
226
|
+
arguments: { pids: [99999] }
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
expect.fail('Should have thrown');
|
|
230
|
+
} catch (error: any) {
|
|
231
|
+
expect(error.message).toContain('Process with PID 99999 not found');
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
it('should handle timeout', async () => {
|
|
236
|
+
const callToolHandler = handlers.get('callTool')!;
|
|
237
|
+
const mockProcess = createMockProcess(12347);
|
|
238
|
+
mockSpawn.mockReturnValue(mockProcess);
|
|
239
|
+
|
|
240
|
+
await callToolHandler({
|
|
241
|
+
params: { name: 'run', arguments: { prompt: 'test', workFolder: '/tmp' } }
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
// Call wait with short timeout
|
|
245
|
+
const waitPromise = callToolHandler({
|
|
246
|
+
params: {
|
|
247
|
+
name: 'wait',
|
|
248
|
+
arguments: {
|
|
249
|
+
pids: [12347],
|
|
250
|
+
timeout: 0.1 // 100ms
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
// Don't emit close event
|
|
256
|
+
|
|
257
|
+
try {
|
|
258
|
+
await waitPromise;
|
|
259
|
+
expect.fail('Should have thrown');
|
|
260
|
+
} catch (error: any) {
|
|
261
|
+
expect(error.message).toContain('Timed out');
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
});
|