@probelabs/probe 0.6.0-rc100
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/README.md +583 -0
- package/bin/.gitkeep +0 -0
- package/bin/probe +158 -0
- package/bin/probe-binary +0 -0
- package/build/agent/ProbeAgent.d.ts +199 -0
- package/build/agent/ProbeAgent.js +1486 -0
- package/build/agent/acp/README.md +347 -0
- package/build/agent/acp/connection.js +237 -0
- package/build/agent/acp/connection.test.js +311 -0
- package/build/agent/acp/examples/simple-client.js +212 -0
- package/build/agent/acp/examples/tool-lifecycle.js +230 -0
- package/build/agent/acp/final-test.js +173 -0
- package/build/agent/acp/index.js +5 -0
- package/build/agent/acp/integration.test.js +385 -0
- package/build/agent/acp/manual-test.js +410 -0
- package/build/agent/acp/protocol-test.js +190 -0
- package/build/agent/acp/server.js +448 -0
- package/build/agent/acp/server.test.js +371 -0
- package/build/agent/acp/test-runner.js +216 -0
- package/build/agent/acp/test-utils/README.md +315 -0
- package/build/agent/acp/test-utils/acp-tester.js +484 -0
- package/build/agent/acp/test-utils/mock-acp-client.js +434 -0
- package/build/agent/acp/tools.js +368 -0
- package/build/agent/acp/tools.test.js +334 -0
- package/build/agent/acp/types.js +218 -0
- package/build/agent/acp/types.test.js +327 -0
- package/build/agent/appTracer.js +360 -0
- package/build/agent/fileSpanExporter.js +169 -0
- package/build/agent/index.js +7426 -0
- package/build/agent/mcp/client.js +338 -0
- package/build/agent/mcp/config.js +313 -0
- package/build/agent/mcp/index.js +64 -0
- package/build/agent/mcp/xmlBridge.js +371 -0
- package/build/agent/mockProvider.js +53 -0
- package/build/agent/probeTool.js +257 -0
- package/build/agent/schemaUtils.js +1726 -0
- package/build/agent/simpleTelemetry.js +267 -0
- package/build/agent/telemetry.js +225 -0
- package/build/agent/tokenCounter.js +395 -0
- package/build/agent/tools.js +163 -0
- package/build/cli.js +49 -0
- package/build/delegate.js +267 -0
- package/build/directory-resolver.js +237 -0
- package/build/downloader.js +750 -0
- package/build/extract.js +149 -0
- package/build/index.js +70 -0
- package/build/mcp/index.js +514 -0
- package/build/mcp/index.ts +608 -0
- package/build/query.js +116 -0
- package/build/search.js +247 -0
- package/build/tools/common.js +410 -0
- package/build/tools/index.js +40 -0
- package/build/tools/langchain.js +88 -0
- package/build/tools/system-message.js +121 -0
- package/build/tools/vercel.js +271 -0
- package/build/utils/file-lister.js +193 -0
- package/build/utils.js +128 -0
- package/cjs/agent/ProbeAgent.cjs +5829 -0
- package/cjs/index.cjs +6217 -0
- package/cjs/package.json +3 -0
- package/index.d.ts +401 -0
- package/package.json +114 -0
- package/scripts/postinstall.js +172 -0
- package/src/agent/ProbeAgent.d.ts +199 -0
- package/src/agent/ProbeAgent.js +1486 -0
- package/src/agent/acp/README.md +347 -0
- package/src/agent/acp/connection.js +237 -0
- package/src/agent/acp/connection.test.js +311 -0
- package/src/agent/acp/examples/simple-client.js +212 -0
- package/src/agent/acp/examples/tool-lifecycle.js +230 -0
- package/src/agent/acp/final-test.js +173 -0
- package/src/agent/acp/index.js +5 -0
- package/src/agent/acp/integration.test.js +385 -0
- package/src/agent/acp/manual-test.js +410 -0
- package/src/agent/acp/protocol-test.js +190 -0
- package/src/agent/acp/server.js +448 -0
- package/src/agent/acp/server.test.js +371 -0
- package/src/agent/acp/test-runner.js +216 -0
- package/src/agent/acp/test-utils/README.md +315 -0
- package/src/agent/acp/test-utils/acp-tester.js +484 -0
- package/src/agent/acp/test-utils/mock-acp-client.js +434 -0
- package/src/agent/acp/tools.js +368 -0
- package/src/agent/acp/tools.test.js +334 -0
- package/src/agent/acp/types.js +218 -0
- package/src/agent/acp/types.test.js +327 -0
- package/src/agent/appTracer.js +360 -0
- package/src/agent/fileSpanExporter.js +169 -0
- package/src/agent/index.js +813 -0
- package/src/agent/mcp/client.js +338 -0
- package/src/agent/mcp/config.js +313 -0
- package/src/agent/mcp/index.js +64 -0
- package/src/agent/mcp/xmlBridge.js +371 -0
- package/src/agent/mockProvider.js +53 -0
- package/src/agent/probeTool.js +257 -0
- package/src/agent/schemaUtils.js +1726 -0
- package/src/agent/simpleTelemetry.js +267 -0
- package/src/agent/telemetry.js +225 -0
- package/src/agent/tokenCounter.js +395 -0
- package/src/agent/tools.js +163 -0
- package/src/cli.js +49 -0
- package/src/delegate.js +267 -0
- package/src/directory-resolver.js +237 -0
- package/src/downloader.js +750 -0
- package/src/extract.js +149 -0
- package/src/index.js +70 -0
- package/src/mcp/index.ts +608 -0
- package/src/query.js +116 -0
- package/src/search.js +247 -0
- package/src/tools/common.js +410 -0
- package/src/tools/index.js +40 -0
- package/src/tools/langchain.js +88 -0
- package/src/tools/system-message.js +121 -0
- package/src/tools/vercel.js +271 -0
- package/src/utils/file-lister.js +193 -0
- package/src/utils.js +128 -0
|
@@ -0,0 +1,484 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ACP Test Utility - A comprehensive testing tool for Agent Client Protocol implementations
|
|
5
|
+
*
|
|
6
|
+
* This utility can test any ACP-compliant agent by running a standardized test suite
|
|
7
|
+
* that verifies protocol compliance, error handling, and session management.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* acp-tester --agent-command "node my-agent.js --acp"
|
|
11
|
+
* acp-tester --agent-command "my-agent-binary"
|
|
12
|
+
* acp-tester --agent-command "python agent.py" --timeout 30
|
|
13
|
+
*
|
|
14
|
+
* Features:
|
|
15
|
+
* - Protocol compliance testing
|
|
16
|
+
* - Session management verification
|
|
17
|
+
* - Error handling validation
|
|
18
|
+
* - Tool capability testing
|
|
19
|
+
* - Performance benchmarking
|
|
20
|
+
* - JSON-RPC 2.0 validation
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import { spawn } from 'child_process';
|
|
24
|
+
import { randomUUID } from 'crypto';
|
|
25
|
+
|
|
26
|
+
class ACPTester {
|
|
27
|
+
constructor(options = {}) {
|
|
28
|
+
this.options = {
|
|
29
|
+
timeout: options.timeout || 30000,
|
|
30
|
+
verbose: options.verbose || false,
|
|
31
|
+
agentCommand: options.agentCommand,
|
|
32
|
+
...options
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
this.agent = null;
|
|
36
|
+
this.messageId = 1;
|
|
37
|
+
this.pendingRequests = new Map();
|
|
38
|
+
this.buffer = '';
|
|
39
|
+
this.testResults = [];
|
|
40
|
+
this.startTime = null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
log(level, message, data = null) {
|
|
44
|
+
const timestamp = new Date().toISOString();
|
|
45
|
+
const prefix = {
|
|
46
|
+
'info': 'š',
|
|
47
|
+
'success': 'ā
',
|
|
48
|
+
'error': 'ā',
|
|
49
|
+
'warning': 'ā ļø',
|
|
50
|
+
'debug': 'š'
|
|
51
|
+
}[level] || 'š';
|
|
52
|
+
|
|
53
|
+
console.log(`${prefix} ${message}`);
|
|
54
|
+
|
|
55
|
+
if (data && this.options.verbose) {
|
|
56
|
+
console.log(` ${JSON.stringify(data, null, 2)}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async startAgent() {
|
|
61
|
+
if (!this.options.agentCommand) {
|
|
62
|
+
throw new Error('Agent command required. Use --agent-command "your-agent-command"');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
this.log('info', `Starting agent: ${this.options.agentCommand}`);
|
|
66
|
+
|
|
67
|
+
const parts = this.options.agentCommand.split(' ');
|
|
68
|
+
const command = parts[0];
|
|
69
|
+
const args = parts.slice(1);
|
|
70
|
+
|
|
71
|
+
this.agent = spawn(command, args, {
|
|
72
|
+
stdio: ['pipe', 'pipe', 'pipe']
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// Handle agent output
|
|
76
|
+
this.agent.stdout.on('data', (data) => {
|
|
77
|
+
this.buffer += data.toString();
|
|
78
|
+
this.processBuffer();
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
this.agent.stderr.on('data', (data) => {
|
|
82
|
+
if (this.options.verbose) {
|
|
83
|
+
const output = data.toString().trim();
|
|
84
|
+
if (output) {
|
|
85
|
+
this.log('debug', `Agent stderr: ${output}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
this.agent.on('close', (code) => {
|
|
91
|
+
this.log('info', `Agent closed with code ${code}`);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
this.agent.on('error', (error) => {
|
|
95
|
+
this.log('error', `Agent spawn error: ${error.message}`);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// Wait for agent to start
|
|
99
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
processBuffer() {
|
|
103
|
+
const lines = this.buffer.split('\n');
|
|
104
|
+
this.buffer = lines.pop() || '';
|
|
105
|
+
|
|
106
|
+
for (const line of lines) {
|
|
107
|
+
if (line.trim()) {
|
|
108
|
+
try {
|
|
109
|
+
const message = JSON.parse(line);
|
|
110
|
+
this.handleMessage(message);
|
|
111
|
+
} catch (error) {
|
|
112
|
+
if (this.options.verbose) {
|
|
113
|
+
this.log('warning', `Invalid JSON from agent: ${line.substring(0, 100)}...`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
handleMessage(message) {
|
|
121
|
+
if (message.id && this.pendingRequests.has(message.id)) {
|
|
122
|
+
// Response to our request
|
|
123
|
+
const { resolve, reject } = this.pendingRequests.get(message.id);
|
|
124
|
+
this.pendingRequests.delete(message.id);
|
|
125
|
+
|
|
126
|
+
if (message.error) {
|
|
127
|
+
reject(new ACPError(message.error.code, message.error.message, message.error.data));
|
|
128
|
+
} else {
|
|
129
|
+
resolve(message.result);
|
|
130
|
+
}
|
|
131
|
+
} else if (message.method && !message.id) {
|
|
132
|
+
// Notification from agent
|
|
133
|
+
this.log('debug', `Notification: ${message.method}`, message.params);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async sendRequest(method, params = null, description = '') {
|
|
138
|
+
const id = this.messageId++;
|
|
139
|
+
const message = {
|
|
140
|
+
jsonrpc: '2.0',
|
|
141
|
+
method,
|
|
142
|
+
id
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
if (params !== null) {
|
|
146
|
+
message.params = params;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (description && this.options.verbose) {
|
|
150
|
+
this.log('debug', `Sending: ${description}`);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return new Promise((resolve, reject) => {
|
|
154
|
+
this.pendingRequests.set(id, { resolve, reject });
|
|
155
|
+
|
|
156
|
+
// Send message
|
|
157
|
+
this.agent.stdin.write(JSON.stringify(message) + '\n');
|
|
158
|
+
|
|
159
|
+
// Set timeout
|
|
160
|
+
const timeoutId = setTimeout(() => {
|
|
161
|
+
if (this.pendingRequests.has(id)) {
|
|
162
|
+
this.pendingRequests.delete(id);
|
|
163
|
+
reject(new Error(`Request timeout after ${this.options.timeout}ms`));
|
|
164
|
+
}
|
|
165
|
+
}, this.options.timeout);
|
|
166
|
+
|
|
167
|
+
// Clear timeout when request completes
|
|
168
|
+
const originalResolve = resolve;
|
|
169
|
+
const originalReject = reject;
|
|
170
|
+
|
|
171
|
+
this.pendingRequests.set(id, {
|
|
172
|
+
resolve: (result) => {
|
|
173
|
+
clearTimeout(timeoutId);
|
|
174
|
+
originalResolve(result);
|
|
175
|
+
},
|
|
176
|
+
reject: (error) => {
|
|
177
|
+
clearTimeout(timeoutId);
|
|
178
|
+
originalReject(error);
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
async runTest(testName, testFn) {
|
|
185
|
+
const startTime = Date.now();
|
|
186
|
+
|
|
187
|
+
try {
|
|
188
|
+
this.log('info', `Running: ${testName}`);
|
|
189
|
+
const result = await testFn();
|
|
190
|
+
const duration = Date.now() - startTime;
|
|
191
|
+
|
|
192
|
+
this.log('success', `${testName} (${duration}ms)`);
|
|
193
|
+
this.testResults.push({
|
|
194
|
+
name: testName,
|
|
195
|
+
status: 'PASSED',
|
|
196
|
+
duration,
|
|
197
|
+
result
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
return result;
|
|
201
|
+
} catch (error) {
|
|
202
|
+
const duration = Date.now() - startTime;
|
|
203
|
+
|
|
204
|
+
this.log('error', `${testName} (${duration}ms): ${error.message}`);
|
|
205
|
+
this.testResults.push({
|
|
206
|
+
name: testName,
|
|
207
|
+
status: 'FAILED',
|
|
208
|
+
duration,
|
|
209
|
+
error: error.message
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
throw error;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Test Suite
|
|
217
|
+
async testInitialize() {
|
|
218
|
+
return this.runTest('Protocol Initialize', async () => {
|
|
219
|
+
const result = await this.sendRequest('initialize', {
|
|
220
|
+
protocolVersion: '1'
|
|
221
|
+
}, 'Initialize protocol');
|
|
222
|
+
|
|
223
|
+
// Validate response structure
|
|
224
|
+
this.validateInitializeResponse(result);
|
|
225
|
+
|
|
226
|
+
return {
|
|
227
|
+
protocolVersion: result.protocolVersion,
|
|
228
|
+
serverName: result.serverInfo?.name,
|
|
229
|
+
toolCount: result.capabilities?.tools?.length || 0,
|
|
230
|
+
hasSessionManagement: result.capabilities?.sessionManagement || false
|
|
231
|
+
};
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
async testSessionManagement() {
|
|
236
|
+
return this.runTest('Session Management', async () => {
|
|
237
|
+
// Create session
|
|
238
|
+
const createResult = await this.sendRequest('newSession', {}, 'Create session');
|
|
239
|
+
this.validateSessionResponse(createResult);
|
|
240
|
+
|
|
241
|
+
const sessionId = createResult.sessionId;
|
|
242
|
+
|
|
243
|
+
// Load session
|
|
244
|
+
const loadResult = await this.sendRequest('loadSession', { sessionId }, 'Load session');
|
|
245
|
+
this.validateLoadSessionResponse(loadResult, sessionId);
|
|
246
|
+
|
|
247
|
+
return { sessionId, created: createResult, loaded: loadResult };
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
async testErrorHandling() {
|
|
252
|
+
return this.runTest('Error Handling', async () => {
|
|
253
|
+
const errors = [];
|
|
254
|
+
|
|
255
|
+
// Test unknown method
|
|
256
|
+
try {
|
|
257
|
+
await this.sendRequest('unknownMethod', {}, 'Unknown method');
|
|
258
|
+
errors.push('Should have failed for unknown method');
|
|
259
|
+
} catch (error) {
|
|
260
|
+
if (error.code === -32601) {
|
|
261
|
+
// Correct error code
|
|
262
|
+
} else {
|
|
263
|
+
errors.push(`Wrong error code for unknown method: ${error.code}`);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Test invalid params
|
|
268
|
+
try {
|
|
269
|
+
await this.sendRequest('loadSession', { sessionId: 'invalid' }, 'Invalid session');
|
|
270
|
+
errors.push('Should have failed for invalid session');
|
|
271
|
+
} catch (error) {
|
|
272
|
+
// Should get some error (specific code may vary)
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if (errors.length > 0) {
|
|
276
|
+
throw new Error(`Error handling issues: ${errors.join(', ')}`);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return { errorTestsPassed: 2 };
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
async testPerformance() {
|
|
284
|
+
return this.runTest('Performance Benchmark', async () => {
|
|
285
|
+
const iterations = 5;
|
|
286
|
+
const times = [];
|
|
287
|
+
|
|
288
|
+
for (let i = 0; i < iterations; i++) {
|
|
289
|
+
const start = Date.now();
|
|
290
|
+
|
|
291
|
+
await this.sendRequest('newSession', {}, `Performance test ${i + 1}`);
|
|
292
|
+
|
|
293
|
+
const duration = Date.now() - start;
|
|
294
|
+
times.push(duration);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const avgTime = times.reduce((a, b) => a + b, 0) / times.length;
|
|
298
|
+
const minTime = Math.min(...times);
|
|
299
|
+
const maxTime = Math.max(...times);
|
|
300
|
+
|
|
301
|
+
return {
|
|
302
|
+
averageResponseTime: Math.round(avgTime),
|
|
303
|
+
minResponseTime: minTime,
|
|
304
|
+
maxResponseTime: maxTime,
|
|
305
|
+
iterations
|
|
306
|
+
};
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Validation helpers
|
|
311
|
+
validateInitializeResponse(result) {
|
|
312
|
+
if (!result.protocolVersion) {
|
|
313
|
+
throw new Error('Missing protocolVersion in initialize response');
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
if (!result.serverInfo || !result.serverInfo.name) {
|
|
317
|
+
throw new Error('Missing serverInfo.name in initialize response');
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
if (!result.capabilities) {
|
|
321
|
+
throw new Error('Missing capabilities in initialize response');
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
validateSessionResponse(result) {
|
|
326
|
+
if (!result.sessionId) {
|
|
327
|
+
throw new Error('Missing sessionId in newSession response');
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
if (typeof result.sessionId !== 'string') {
|
|
331
|
+
throw new Error('sessionId must be a string');
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
validateLoadSessionResponse(result, expectedSessionId) {
|
|
336
|
+
if (result.id !== expectedSessionId) {
|
|
337
|
+
throw new Error('loadSession returned wrong session ID');
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
async runAllTests() {
|
|
342
|
+
this.startTime = Date.now();
|
|
343
|
+
|
|
344
|
+
this.log('info', 'ACP Protocol Compliance Test Suite');
|
|
345
|
+
this.log('info', '=' .repeat(40));
|
|
346
|
+
|
|
347
|
+
try {
|
|
348
|
+
await this.startAgent();
|
|
349
|
+
|
|
350
|
+
// Required tests
|
|
351
|
+
const initResult = await this.testInitialize();
|
|
352
|
+
await this.testSessionManagement();
|
|
353
|
+
await this.testErrorHandling();
|
|
354
|
+
|
|
355
|
+
// Performance benchmark
|
|
356
|
+
await this.testPerformance();
|
|
357
|
+
|
|
358
|
+
this.showResults();
|
|
359
|
+
|
|
360
|
+
} catch (error) {
|
|
361
|
+
this.log('error', `Test suite failed: ${error.message}`);
|
|
362
|
+
this.showResults();
|
|
363
|
+
return false;
|
|
364
|
+
} finally {
|
|
365
|
+
this.cleanup();
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
return this.testResults.every(r => r.status === 'PASSED');
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
showResults() {
|
|
372
|
+
const totalTime = Date.now() - this.startTime;
|
|
373
|
+
const passed = this.testResults.filter(r => r.status === 'PASSED').length;
|
|
374
|
+
const failed = this.testResults.filter(r => r.status === 'FAILED').length;
|
|
375
|
+
|
|
376
|
+
this.log('info', '');
|
|
377
|
+
this.log('info', 'Test Results Summary');
|
|
378
|
+
this.log('info', '=' .repeat(20));
|
|
379
|
+
|
|
380
|
+
for (const result of this.testResults) {
|
|
381
|
+
const icon = result.status === 'PASSED' ? 'ā
' : 'ā';
|
|
382
|
+
console.log(`${icon} ${result.name} (${result.duration}ms)`);
|
|
383
|
+
if (result.error) {
|
|
384
|
+
console.log(` Error: ${result.error}`);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
this.log('info', '');
|
|
389
|
+
this.log('info', `Total time: ${totalTime}ms`);
|
|
390
|
+
this.log('info', `Passed: ${passed}`);
|
|
391
|
+
this.log('info', `Failed: ${failed}`);
|
|
392
|
+
this.log('info', `Total: ${this.testResults.length}`);
|
|
393
|
+
|
|
394
|
+
if (failed === 0) {
|
|
395
|
+
this.log('success', 'Agent is ACP compliant! š');
|
|
396
|
+
} else {
|
|
397
|
+
this.log('error', `Agent failed ${failed} compliance test(s)`);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
cleanup() {
|
|
402
|
+
if (this.agent) {
|
|
403
|
+
this.agent.stdin.end();
|
|
404
|
+
this.agent.kill();
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// Custom error class for ACP errors
|
|
410
|
+
class ACPError extends Error {
|
|
411
|
+
constructor(code, message, data = null) {
|
|
412
|
+
super(message);
|
|
413
|
+
this.code = code;
|
|
414
|
+
this.data = data;
|
|
415
|
+
this.name = 'ACPError';
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// CLI handling
|
|
420
|
+
async function main() {
|
|
421
|
+
const args = process.argv.slice(2);
|
|
422
|
+
const options = {};
|
|
423
|
+
|
|
424
|
+
for (let i = 0; i < args.length; i++) {
|
|
425
|
+
const arg = args[i];
|
|
426
|
+
|
|
427
|
+
if (arg === '--agent-command' && i + 1 < args.length) {
|
|
428
|
+
options.agentCommand = args[++i];
|
|
429
|
+
} else if (arg === '--timeout' && i + 1 < args.length) {
|
|
430
|
+
options.timeout = parseInt(args[++i], 10) * 1000; // Convert to ms
|
|
431
|
+
} else if (arg === '--verbose' || arg === '-v') {
|
|
432
|
+
options.verbose = true;
|
|
433
|
+
} else if (arg === '--help' || arg === '-h') {
|
|
434
|
+
console.log(`
|
|
435
|
+
ACP Tester - Agent Client Protocol Compliance Testing Tool
|
|
436
|
+
|
|
437
|
+
Usage:
|
|
438
|
+
acp-tester --agent-command "command to start your agent"
|
|
439
|
+
|
|
440
|
+
Options:
|
|
441
|
+
--agent-command <cmd> Command to start the agent (required)
|
|
442
|
+
--timeout <seconds> Request timeout in seconds (default: 30)
|
|
443
|
+
--verbose, -v Enable verbose output
|
|
444
|
+
--help, -h Show this help
|
|
445
|
+
|
|
446
|
+
Examples:
|
|
447
|
+
acp-tester --agent-command "node my-agent.js --acp"
|
|
448
|
+
acp-tester --agent-command "python agent.py" --timeout 60 --verbose
|
|
449
|
+
acp-tester --agent-command "./my-agent-binary"
|
|
450
|
+
|
|
451
|
+
Test Suite:
|
|
452
|
+
⢠Protocol initialization and capabilities
|
|
453
|
+
⢠Session management (create/load)
|
|
454
|
+
⢠Error handling and edge cases
|
|
455
|
+
⢠Performance benchmarking
|
|
456
|
+
⢠JSON-RPC 2.0 compliance validation
|
|
457
|
+
`);
|
|
458
|
+
process.exit(0);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
if (!options.agentCommand) {
|
|
463
|
+
console.error('ā Error: --agent-command is required');
|
|
464
|
+
console.error('Use --help for usage information');
|
|
465
|
+
process.exit(1);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
const tester = new ACPTester(options);
|
|
469
|
+
const success = await tester.runAllTests();
|
|
470
|
+
|
|
471
|
+
process.exit(success ? 0 : 1);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// Handle Ctrl+C gracefully
|
|
475
|
+
process.on('SIGINT', () => {
|
|
476
|
+
console.log('\nā¹ļø Test interrupted');
|
|
477
|
+
process.exit(0);
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
481
|
+
main().catch(console.error);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
export { ACPTester };
|