@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.
Files changed (115) hide show
  1. package/README.md +583 -0
  2. package/bin/.gitkeep +0 -0
  3. package/bin/probe +158 -0
  4. package/bin/probe-binary +0 -0
  5. package/build/agent/ProbeAgent.d.ts +199 -0
  6. package/build/agent/ProbeAgent.js +1486 -0
  7. package/build/agent/acp/README.md +347 -0
  8. package/build/agent/acp/connection.js +237 -0
  9. package/build/agent/acp/connection.test.js +311 -0
  10. package/build/agent/acp/examples/simple-client.js +212 -0
  11. package/build/agent/acp/examples/tool-lifecycle.js +230 -0
  12. package/build/agent/acp/final-test.js +173 -0
  13. package/build/agent/acp/index.js +5 -0
  14. package/build/agent/acp/integration.test.js +385 -0
  15. package/build/agent/acp/manual-test.js +410 -0
  16. package/build/agent/acp/protocol-test.js +190 -0
  17. package/build/agent/acp/server.js +448 -0
  18. package/build/agent/acp/server.test.js +371 -0
  19. package/build/agent/acp/test-runner.js +216 -0
  20. package/build/agent/acp/test-utils/README.md +315 -0
  21. package/build/agent/acp/test-utils/acp-tester.js +484 -0
  22. package/build/agent/acp/test-utils/mock-acp-client.js +434 -0
  23. package/build/agent/acp/tools.js +368 -0
  24. package/build/agent/acp/tools.test.js +334 -0
  25. package/build/agent/acp/types.js +218 -0
  26. package/build/agent/acp/types.test.js +327 -0
  27. package/build/agent/appTracer.js +360 -0
  28. package/build/agent/fileSpanExporter.js +169 -0
  29. package/build/agent/index.js +7426 -0
  30. package/build/agent/mcp/client.js +338 -0
  31. package/build/agent/mcp/config.js +313 -0
  32. package/build/agent/mcp/index.js +64 -0
  33. package/build/agent/mcp/xmlBridge.js +371 -0
  34. package/build/agent/mockProvider.js +53 -0
  35. package/build/agent/probeTool.js +257 -0
  36. package/build/agent/schemaUtils.js +1726 -0
  37. package/build/agent/simpleTelemetry.js +267 -0
  38. package/build/agent/telemetry.js +225 -0
  39. package/build/agent/tokenCounter.js +395 -0
  40. package/build/agent/tools.js +163 -0
  41. package/build/cli.js +49 -0
  42. package/build/delegate.js +267 -0
  43. package/build/directory-resolver.js +237 -0
  44. package/build/downloader.js +750 -0
  45. package/build/extract.js +149 -0
  46. package/build/index.js +70 -0
  47. package/build/mcp/index.js +514 -0
  48. package/build/mcp/index.ts +608 -0
  49. package/build/query.js +116 -0
  50. package/build/search.js +247 -0
  51. package/build/tools/common.js +410 -0
  52. package/build/tools/index.js +40 -0
  53. package/build/tools/langchain.js +88 -0
  54. package/build/tools/system-message.js +121 -0
  55. package/build/tools/vercel.js +271 -0
  56. package/build/utils/file-lister.js +193 -0
  57. package/build/utils.js +128 -0
  58. package/cjs/agent/ProbeAgent.cjs +5829 -0
  59. package/cjs/index.cjs +6217 -0
  60. package/cjs/package.json +3 -0
  61. package/index.d.ts +401 -0
  62. package/package.json +114 -0
  63. package/scripts/postinstall.js +172 -0
  64. package/src/agent/ProbeAgent.d.ts +199 -0
  65. package/src/agent/ProbeAgent.js +1486 -0
  66. package/src/agent/acp/README.md +347 -0
  67. package/src/agent/acp/connection.js +237 -0
  68. package/src/agent/acp/connection.test.js +311 -0
  69. package/src/agent/acp/examples/simple-client.js +212 -0
  70. package/src/agent/acp/examples/tool-lifecycle.js +230 -0
  71. package/src/agent/acp/final-test.js +173 -0
  72. package/src/agent/acp/index.js +5 -0
  73. package/src/agent/acp/integration.test.js +385 -0
  74. package/src/agent/acp/manual-test.js +410 -0
  75. package/src/agent/acp/protocol-test.js +190 -0
  76. package/src/agent/acp/server.js +448 -0
  77. package/src/agent/acp/server.test.js +371 -0
  78. package/src/agent/acp/test-runner.js +216 -0
  79. package/src/agent/acp/test-utils/README.md +315 -0
  80. package/src/agent/acp/test-utils/acp-tester.js +484 -0
  81. package/src/agent/acp/test-utils/mock-acp-client.js +434 -0
  82. package/src/agent/acp/tools.js +368 -0
  83. package/src/agent/acp/tools.test.js +334 -0
  84. package/src/agent/acp/types.js +218 -0
  85. package/src/agent/acp/types.test.js +327 -0
  86. package/src/agent/appTracer.js +360 -0
  87. package/src/agent/fileSpanExporter.js +169 -0
  88. package/src/agent/index.js +813 -0
  89. package/src/agent/mcp/client.js +338 -0
  90. package/src/agent/mcp/config.js +313 -0
  91. package/src/agent/mcp/index.js +64 -0
  92. package/src/agent/mcp/xmlBridge.js +371 -0
  93. package/src/agent/mockProvider.js +53 -0
  94. package/src/agent/probeTool.js +257 -0
  95. package/src/agent/schemaUtils.js +1726 -0
  96. package/src/agent/simpleTelemetry.js +267 -0
  97. package/src/agent/telemetry.js +225 -0
  98. package/src/agent/tokenCounter.js +395 -0
  99. package/src/agent/tools.js +163 -0
  100. package/src/cli.js +49 -0
  101. package/src/delegate.js +267 -0
  102. package/src/directory-resolver.js +237 -0
  103. package/src/downloader.js +750 -0
  104. package/src/extract.js +149 -0
  105. package/src/index.js +70 -0
  106. package/src/mcp/index.ts +608 -0
  107. package/src/query.js +116 -0
  108. package/src/search.js +247 -0
  109. package/src/tools/common.js +410 -0
  110. package/src/tools/index.js +40 -0
  111. package/src/tools/langchain.js +88 -0
  112. package/src/tools/system-message.js +121 -0
  113. package/src/tools/vercel.js +271 -0
  114. package/src/utils/file-lister.js +193 -0
  115. 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 };