@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,311 @@
1
+ // Tests for ACP Connection
2
+ import { jest } from '@jest/globals';
3
+ import { EventEmitter } from 'events';
4
+ import { ACPConnection } from './connection.js';
5
+ import { ErrorCode, RequestMethod } from './types.js';
6
+
7
+ // Mock streams
8
+ class MockStream extends EventEmitter {
9
+ constructor() {
10
+ super();
11
+ this.encoding = null;
12
+ this.writtenData = [];
13
+ }
14
+
15
+ setEncoding(encoding) {
16
+ this.encoding = encoding;
17
+ }
18
+
19
+ write(data) {
20
+ this.writtenData.push(data);
21
+ return true;
22
+ }
23
+ }
24
+
25
+ describe('ACPConnection', () => {
26
+ let inputStream, outputStream, connection;
27
+
28
+ beforeEach(() => {
29
+ inputStream = new MockStream();
30
+ outputStream = new MockStream();
31
+ connection = new ACPConnection(inputStream, outputStream);
32
+ });
33
+
34
+ afterEach(() => {
35
+ // Clean up fake timers if they were used
36
+ if (jest.isMockFunction(setTimeout)) {
37
+ jest.useRealTimers();
38
+ }
39
+ connection.close();
40
+ });
41
+
42
+ describe('initialization', () => {
43
+ test('should initialize with correct default values', () => {
44
+ expect(connection.isConnected).toBe(false);
45
+ expect(connection.messageId).toBe(1);
46
+ expect(connection.buffer).toBe('');
47
+ expect(connection.pendingRequests.size).toBe(0);
48
+ });
49
+
50
+ test('should setup streams correctly', () => {
51
+ connection.start();
52
+ expect(connection.isConnected).toBe(true);
53
+ expect(inputStream.encoding).toBe('utf8');
54
+ });
55
+ });
56
+
57
+ describe('message handling', () => {
58
+ beforeEach(() => {
59
+ connection.start();
60
+ });
61
+
62
+ test('should parse valid JSON-RPC messages', (done) => {
63
+ const message = {
64
+ jsonrpc: '2.0',
65
+ method: RequestMethod.INITIALIZE,
66
+ id: 1,
67
+ params: { protocolVersion: '1' }
68
+ };
69
+
70
+ connection.on('request', (receivedMessage) => {
71
+ expect(receivedMessage).toEqual(message);
72
+ done();
73
+ });
74
+
75
+ inputStream.emit('data', JSON.stringify(message) + '\n');
76
+ });
77
+
78
+ test('should handle invalid JSON gracefully', () => {
79
+ inputStream.emit('data', 'invalid json\n');
80
+
81
+ // Should send parse error
82
+ expect(outputStream.writtenData).toHaveLength(1);
83
+ const response = JSON.parse(outputStream.writtenData[0]);
84
+ expect(response.error.code).toBe(ErrorCode.PARSE_ERROR);
85
+ });
86
+
87
+ test('should handle notifications', (done) => {
88
+ const notification = {
89
+ jsonrpc: '2.0',
90
+ method: 'someNotification',
91
+ params: { data: 'test' }
92
+ };
93
+
94
+ connection.on('notification', (receivedMessage) => {
95
+ expect(receivedMessage).toEqual(notification);
96
+ done();
97
+ });
98
+
99
+ inputStream.emit('data', JSON.stringify(notification) + '\n');
100
+ });
101
+
102
+ test('should validate message format', () => {
103
+ const invalidMessage = {
104
+ jsonrpc: '1.0', // Wrong version
105
+ method: 'test'
106
+ };
107
+
108
+ inputStream.emit('data', JSON.stringify(invalidMessage) + '\n');
109
+
110
+ // Should send invalid request error
111
+ expect(outputStream.writtenData).toHaveLength(1);
112
+ const response = JSON.parse(outputStream.writtenData[0]);
113
+ expect(response.error.code).toBe(ErrorCode.INVALID_REQUEST);
114
+ });
115
+ });
116
+
117
+ describe('sending messages', () => {
118
+ beforeEach(() => {
119
+ connection.start();
120
+ });
121
+
122
+ test('should send requests with auto-incrementing IDs', async () => {
123
+ const params = { test: 'data' };
124
+
125
+ // Don't await - we'll resolve it manually
126
+ const requestPromise = connection.sendRequest('testMethod', params);
127
+
128
+ // Check that request was sent
129
+ expect(outputStream.writtenData).toHaveLength(1);
130
+ const sentMessage = JSON.parse(outputStream.writtenData[0]);
131
+ expect(sentMessage).toEqual({
132
+ jsonrpc: '2.0',
133
+ method: 'testMethod',
134
+ params,
135
+ id: 1
136
+ });
137
+
138
+ // Simulate response
139
+ const response = {
140
+ jsonrpc: '2.0',
141
+ id: 1,
142
+ result: { success: true }
143
+ };
144
+
145
+ inputStream.emit('data', JSON.stringify(response) + '\n');
146
+
147
+ const result = await requestPromise;
148
+ expect(result).toEqual({ success: true });
149
+ });
150
+
151
+ test('should send notifications without ID', () => {
152
+ const params = { notification: 'data' };
153
+
154
+ connection.sendNotification('testNotification', params);
155
+
156
+ expect(outputStream.writtenData).toHaveLength(1);
157
+ const sentMessage = JSON.parse(outputStream.writtenData[0]);
158
+ expect(sentMessage).toEqual({
159
+ jsonrpc: '2.0',
160
+ method: 'testNotification',
161
+ params
162
+ });
163
+ expect(sentMessage.id).toBeUndefined();
164
+ });
165
+
166
+ test('should send responses', () => {
167
+ const result = { data: 'test' };
168
+
169
+ connection.sendResponse(123, result);
170
+
171
+ expect(outputStream.writtenData).toHaveLength(1);
172
+ const sentMessage = JSON.parse(outputStream.writtenData[0]);
173
+ expect(sentMessage).toEqual({
174
+ jsonrpc: '2.0',
175
+ id: 123,
176
+ result
177
+ });
178
+ });
179
+
180
+ test('should send errors', () => {
181
+ connection.sendError(456, ErrorCode.INTERNAL_ERROR, 'Test error', { detail: 'test' });
182
+
183
+ expect(outputStream.writtenData).toHaveLength(1);
184
+ const sentMessage = JSON.parse(outputStream.writtenData[0]);
185
+ expect(sentMessage).toEqual({
186
+ jsonrpc: '2.0',
187
+ id: 456,
188
+ error: {
189
+ code: ErrorCode.INTERNAL_ERROR,
190
+ message: 'Test error',
191
+ data: { detail: 'test' }
192
+ }
193
+ });
194
+ });
195
+ });
196
+
197
+ describe('request/response handling', () => {
198
+ beforeEach(() => {
199
+ connection.start();
200
+ });
201
+
202
+ test('should handle response errors', async () => {
203
+ const requestPromise = connection.sendRequest('testMethod');
204
+
205
+ const errorResponse = {
206
+ jsonrpc: '2.0',
207
+ id: 1,
208
+ error: {
209
+ code: ErrorCode.METHOD_NOT_FOUND,
210
+ message: 'Method not found'
211
+ }
212
+ };
213
+
214
+ inputStream.emit('data', JSON.stringify(errorResponse) + '\n');
215
+
216
+ await expect(requestPromise).rejects.toThrow('RPC Error -32601: Method not found');
217
+ });
218
+
219
+ test('should timeout requests', async () => {
220
+ // Set up fake timers before creating the promise
221
+ jest.useFakeTimers();
222
+
223
+ const requestPromise = connection.sendRequest('testMethod');
224
+
225
+ // Fast-forward time past the timeout
226
+ jest.advanceTimersByTime(30000);
227
+
228
+ await expect(requestPromise).rejects.toThrow('Request timeout');
229
+
230
+ jest.useRealTimers();
231
+ });
232
+ });
233
+
234
+ describe('connection management', () => {
235
+ test('should handle disconnection', (done) => {
236
+ connection.start();
237
+
238
+ connection.on('disconnect', () => {
239
+ expect(connection.isConnected).toBe(false);
240
+ done();
241
+ });
242
+
243
+ inputStream.emit('end');
244
+ });
245
+
246
+ test('should reject pending requests on close', async () => {
247
+ connection.start();
248
+ const requestPromise = connection.sendRequest('testMethod');
249
+
250
+ connection.close();
251
+
252
+ await expect(requestPromise).rejects.toThrow('Connection closed');
253
+ });
254
+
255
+ test('should handle stream errors', (done) => {
256
+ connection.start();
257
+
258
+ connection.on('error', (error) => {
259
+ expect(error.message).toBe('Test error');
260
+ done();
261
+ });
262
+
263
+ inputStream.emit('error', new Error('Test error'));
264
+ });
265
+ });
266
+
267
+ describe('buffering', () => {
268
+ beforeEach(() => {
269
+ connection.start();
270
+ });
271
+
272
+ test('should handle partial messages', (done) => {
273
+ const message = {
274
+ jsonrpc: '2.0',
275
+ method: 'test',
276
+ params: { data: 'test' }
277
+ };
278
+
279
+ connection.on('notification', (receivedMessage) => {
280
+ expect(receivedMessage).toEqual(message);
281
+ done();
282
+ });
283
+
284
+ const jsonString = JSON.stringify(message) + '\n';
285
+
286
+ // Send message in chunks
287
+ inputStream.emit('data', jsonString.substring(0, 10));
288
+ inputStream.emit('data', jsonString.substring(10, 20));
289
+ inputStream.emit('data', jsonString.substring(20));
290
+ });
291
+
292
+ test('should handle multiple messages in one chunk', () => {
293
+ const messages = [
294
+ { jsonrpc: '2.0', method: 'test1' },
295
+ { jsonrpc: '2.0', method: 'test2' }
296
+ ];
297
+
298
+ const receivedMessages = [];
299
+ connection.on('notification', (message) => {
300
+ receivedMessages.push(message);
301
+ });
302
+
303
+ const chunk = messages.map(msg => JSON.stringify(msg)).join('\n') + '\n';
304
+ inputStream.emit('data', chunk);
305
+
306
+ expect(receivedMessages).toHaveLength(2);
307
+ expect(receivedMessages[0].method).toBe('test1');
308
+ expect(receivedMessages[1].method).toBe('test2');
309
+ });
310
+ });
311
+ });
@@ -0,0 +1,212 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Simple ACP Client Example
5
+ *
6
+ * This example demonstrates how to communicate with the Probe ACP server
7
+ * using basic JSON-RPC 2.0 messages over stdio.
8
+ *
9
+ * Usage:
10
+ * node simple-client.js
11
+ *
12
+ * Make sure to start the ACP server first:
13
+ * probe agent --acp
14
+ */
15
+
16
+ import { spawn } from 'child_process';
17
+ import { EventEmitter } from 'events';
18
+
19
+ class SimpleACPClient extends EventEmitter {
20
+ constructor() {
21
+ super();
22
+ this.messageId = 1;
23
+ this.pendingRequests = new Map();
24
+ this.sessionId = null;
25
+ this.server = null;
26
+ }
27
+
28
+ async start() {
29
+ console.log('šŸš€ Starting Probe ACP server...');
30
+
31
+ // Spawn the ACP server
32
+ this.server = spawn('node', ['../../../index.js', '--acp'], {
33
+ stdio: ['pipe', 'pipe', 'pipe']
34
+ });
35
+
36
+ // Handle server output (our responses)
37
+ this.server.stdout.on('data', (data) => {
38
+ const lines = data.toString().split('\n').filter(line => line.trim());
39
+ for (const line of lines) {
40
+ try {
41
+ const message = JSON.parse(line);
42
+ this.handleMessage(message);
43
+ } catch (error) {
44
+ console.error('āŒ Failed to parse server message:', line);
45
+ }
46
+ }
47
+ });
48
+
49
+ // Handle server errors
50
+ this.server.stderr.on('data', (data) => {
51
+ const output = data.toString();
52
+ if (output.includes('[ACP]') || output.includes('DEBUG')) {
53
+ console.log('šŸ” Server:', output.trim());
54
+ } else {
55
+ console.error('āš ļø Server error:', output.trim());
56
+ }
57
+ });
58
+
59
+ this.server.on('close', (code) => {
60
+ console.log(`šŸ““ Server closed with code ${code}`);
61
+ });
62
+
63
+ // Wait a bit for server to start
64
+ await new Promise(resolve => setTimeout(resolve, 1000));
65
+ console.log('āœ… Server started\n');
66
+ }
67
+
68
+ handleMessage(message) {
69
+ if (message.id && this.pendingRequests.has(message.id)) {
70
+ // This is a response to our request
71
+ const { resolve, reject } = this.pendingRequests.get(message.id);
72
+ this.pendingRequests.delete(message.id);
73
+
74
+ if (message.error) {
75
+ reject(new Error(`${message.error.code}: ${message.error.message}`));
76
+ } else {
77
+ resolve(message.result);
78
+ }
79
+ } else if (message.method) {
80
+ // This is a notification from the server
81
+ console.log(`šŸ“Ø Notification: ${message.method}`, message.params);
82
+ }
83
+ }
84
+
85
+ async sendRequest(method, params = null) {
86
+ const id = this.messageId++;
87
+ const message = {
88
+ jsonrpc: '2.0',
89
+ method,
90
+ id
91
+ };
92
+
93
+ if (params !== null) {
94
+ message.params = params;
95
+ }
96
+
97
+ return new Promise((resolve, reject) => {
98
+ this.pendingRequests.set(id, { resolve, reject });
99
+
100
+ // Send message to server
101
+ this.server.stdin.write(JSON.stringify(message) + '\n');
102
+
103
+ // Timeout after 30 seconds
104
+ setTimeout(() => {
105
+ if (this.pendingRequests.has(id)) {
106
+ this.pendingRequests.delete(id);
107
+ reject(new Error('Request timeout'));
108
+ }
109
+ }, 30000);
110
+ });
111
+ }
112
+
113
+ async initialize() {
114
+ console.log('šŸ”§ Initializing ACP protocol...');
115
+ const result = await this.sendRequest('initialize', {
116
+ protocolVersion: '1'
117
+ });
118
+
119
+ console.log('āœ… Protocol initialized');
120
+ console.log(` Server: ${result.serverInfo.name} v${result.serverInfo.version}`);
121
+ console.log(` Capabilities: ${result.capabilities.tools.length} tools, sessions: ${result.capabilities.sessionManagement}`);
122
+ console.log();
123
+
124
+ return result;
125
+ }
126
+
127
+ async createSession() {
128
+ console.log('šŸ“ Creating new session...');
129
+ const result = await this.sendRequest('newSession', {});
130
+
131
+ this.sessionId = result.sessionId;
132
+ console.log(`āœ… Session created: ${this.sessionId}`);
133
+ console.log();
134
+
135
+ return result;
136
+ }
137
+
138
+ async sendPrompt(message) {
139
+ if (!this.sessionId) {
140
+ throw new Error('No active session. Create a session first.');
141
+ }
142
+
143
+ console.log(`šŸ’¬ Sending prompt: "${message}"`);
144
+ console.log('šŸ¤– AI is thinking...\n');
145
+
146
+ const result = await this.sendRequest('prompt', {
147
+ sessionId: this.sessionId,
148
+ message
149
+ });
150
+
151
+ // Extract text from content blocks
152
+ const text = result.content
153
+ .filter(block => block.type === 'text')
154
+ .map(block => block.text)
155
+ .join('\n');
156
+
157
+ console.log('šŸ¤– AI Response:');
158
+ console.log('━'.repeat(50));
159
+ console.log(text);
160
+ console.log('━'.repeat(50));
161
+ console.log();
162
+
163
+ return result;
164
+ }
165
+
166
+ async close() {
167
+ console.log('šŸ”“ Closing connection...');
168
+ if (this.server) {
169
+ this.server.stdin.end();
170
+ this.server.kill();
171
+ }
172
+ }
173
+ }
174
+
175
+ // Example usage
176
+ async function main() {
177
+ const client = new SimpleACPClient();
178
+
179
+ try {
180
+ await client.start();
181
+ await client.initialize();
182
+ await client.createSession();
183
+
184
+ // Example conversations
185
+ await client.sendPrompt("What files are in this project?");
186
+
187
+ await new Promise(resolve => setTimeout(resolve, 1000));
188
+
189
+ await client.sendPrompt("Find all functions that handle HTTP requests");
190
+
191
+ await new Promise(resolve => setTimeout(resolve, 1000));
192
+
193
+ await client.sendPrompt("Show me the main entry point of this application");
194
+
195
+ } catch (error) {
196
+ console.error('āŒ Error:', error.message);
197
+ } finally {
198
+ await client.close();
199
+ process.exit(0);
200
+ }
201
+ }
202
+
203
+ // Handle Ctrl+C gracefully
204
+ process.on('SIGINT', async () => {
205
+ console.log('\nšŸ‘‹ Goodbye!');
206
+ process.exit(0);
207
+ });
208
+
209
+ // Run the example
210
+ if (import.meta.url === `file://${process.argv[1]}`) {
211
+ main().catch(console.error);
212
+ }