@probelabs/probe 0.6.0-rc137 → 0.6.0-rc139
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/build/agent/ProbeAgent.js +5 -2
- package/build/agent/index.js +103 -48
- package/build/extract.js +142 -59
- package/cjs/agent/ProbeAgent.cjs +377 -316
- package/cjs/index.cjs +377 -316
- package/index.d.ts +10 -2
- package/package.json +1 -1
- package/src/agent/ProbeAgent.js +5 -2
- package/src/extract.js +142 -59
package/index.d.ts
CHANGED
|
@@ -350,13 +350,21 @@ export interface QueryParams {
|
|
|
350
350
|
*/
|
|
351
351
|
export interface ExtractParams {
|
|
352
352
|
/** Files and line numbers or symbols to extract */
|
|
353
|
-
files
|
|
353
|
+
files?: string[];
|
|
354
|
+
/** Path to a file containing unstructured text to extract file paths from */
|
|
355
|
+
inputFile?: string;
|
|
356
|
+
/** Content to pipe to stdin (e.g., git diff output). Alternative to inputFile or files. */
|
|
357
|
+
content?: string | Buffer;
|
|
354
358
|
/** Path to search in */
|
|
355
359
|
path?: string;
|
|
356
360
|
/** Number of context lines */
|
|
357
361
|
contextLines?: number;
|
|
358
362
|
/** Output format */
|
|
359
|
-
format?: 'markdown' | 'plain' | 'json';
|
|
363
|
+
format?: 'markdown' | 'plain' | 'json' | 'xml' | 'color' | 'outline-xml' | 'outline-diff';
|
|
364
|
+
/** Include test files */
|
|
365
|
+
allowTests?: boolean;
|
|
366
|
+
/** Return results as parsed JSON instead of string */
|
|
367
|
+
json?: boolean;
|
|
360
368
|
/** Session ID */
|
|
361
369
|
sessionId?: string;
|
|
362
370
|
}
|
package/package.json
CHANGED
package/src/agent/ProbeAgent.js
CHANGED
|
@@ -1270,6 +1270,9 @@ When troubleshooting:
|
|
|
1270
1270
|
temperature: 0.3,
|
|
1271
1271
|
});
|
|
1272
1272
|
|
|
1273
|
+
// Get the promise reference BEFORE consuming stream (doesn't lock it)
|
|
1274
|
+
const usagePromise = result.usage;
|
|
1275
|
+
|
|
1273
1276
|
// Collect the streamed response - stream all content for now
|
|
1274
1277
|
for await (const delta of result.textStream) {
|
|
1275
1278
|
assistantResponseContent += delta;
|
|
@@ -1279,8 +1282,8 @@ When troubleshooting:
|
|
|
1279
1282
|
}
|
|
1280
1283
|
}
|
|
1281
1284
|
|
|
1282
|
-
// Record token usage
|
|
1283
|
-
const usage = await
|
|
1285
|
+
// Record token usage - await the promise AFTER stream is consumed
|
|
1286
|
+
const usage = await usagePromise;
|
|
1284
1287
|
if (usage) {
|
|
1285
1288
|
this.tokenCounter.recordUsage(usage, result.experimental_providerMetadata);
|
|
1286
1289
|
}
|
package/src/extract.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* @module extract
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { exec } from 'child_process';
|
|
6
|
+
import { exec, spawn } from 'child_process';
|
|
7
7
|
import { promisify } from 'util';
|
|
8
8
|
import { getBinaryPath, buildCliArgs, escapeString } from './utils.js';
|
|
9
9
|
|
|
@@ -22,13 +22,14 @@ const EXTRACT_FLAG_MAP = {
|
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
24
|
* Extract code blocks from files
|
|
25
|
-
*
|
|
25
|
+
*
|
|
26
26
|
* @param {Object} options - Extract options
|
|
27
27
|
* @param {string[]} [options.files] - Files to extract from (can include line numbers with colon, e.g., "/path/to/file.rs:10")
|
|
28
28
|
* @param {string} [options.inputFile] - Path to a file containing unstructured text to extract file paths from
|
|
29
|
+
* @param {string|Buffer} [options.content] - Content to pipe to stdin (e.g., git diff output). Alternative to inputFile.
|
|
29
30
|
* @param {boolean} [options.allowTests] - Include test files
|
|
30
31
|
* @param {number} [options.contextLines] - Number of context lines to include
|
|
31
|
-
* @param {string} [options.format] - Output format ('markdown', 'plain', 'json')
|
|
32
|
+
* @param {string} [options.format] - Output format ('markdown', 'plain', 'json', 'xml', 'color', 'outline-xml', 'outline-diff')
|
|
32
33
|
* @param {Object} [options.binaryOptions] - Options for getting the binary
|
|
33
34
|
* @param {boolean} [options.binaryOptions.forceDownload] - Force download even if binary exists
|
|
34
35
|
* @param {string} [options.binaryOptions.version] - Specific version to download
|
|
@@ -41,16 +42,22 @@ export async function extract(options) {
|
|
|
41
42
|
throw new Error('Options object is required');
|
|
42
43
|
}
|
|
43
44
|
|
|
44
|
-
// Either files or
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
// Either files, inputFile, or content must be provided
|
|
46
|
+
const hasFiles = options.files && Array.isArray(options.files) && options.files.length > 0;
|
|
47
|
+
const hasInputFile = !!options.inputFile;
|
|
48
|
+
const hasContent = options.content !== undefined && options.content !== null;
|
|
49
|
+
|
|
50
|
+
if (!hasFiles && !hasInputFile && !hasContent) {
|
|
51
|
+
throw new Error('Either files array, inputFile, or content must be provided');
|
|
47
52
|
}
|
|
48
53
|
|
|
49
54
|
// Get the binary path
|
|
50
55
|
const binaryPath = await getBinaryPath(options.binaryOptions || {});
|
|
51
56
|
|
|
52
|
-
// Build CLI arguments from options
|
|
53
|
-
const
|
|
57
|
+
// Build CLI arguments from options (excluding content which goes via stdin)
|
|
58
|
+
const filteredOptions = { ...options };
|
|
59
|
+
delete filteredOptions.content;
|
|
60
|
+
const cliArgs = buildCliArgs(filteredOptions, EXTRACT_FLAG_MAP);
|
|
54
61
|
|
|
55
62
|
// If json option is true, override format to json
|
|
56
63
|
if (options.json && !options.format) {
|
|
@@ -58,7 +65,7 @@ export async function extract(options) {
|
|
|
58
65
|
}
|
|
59
66
|
|
|
60
67
|
// Add files as positional arguments if provided
|
|
61
|
-
if (
|
|
68
|
+
if (hasFiles) {
|
|
62
69
|
for (const file of options.files) {
|
|
63
70
|
cliArgs.push(escapeString(file));
|
|
64
71
|
}
|
|
@@ -71,6 +78,7 @@ export async function extract(options) {
|
|
|
71
78
|
logMessage += ` files="${options.files.join(', ')}"`;
|
|
72
79
|
}
|
|
73
80
|
if (options.inputFile) logMessage += ` inputFile="${options.inputFile}"`;
|
|
81
|
+
if (options.content) logMessage += ` content=(${typeof options.content === 'string' ? options.content.length : options.content.byteLength} bytes)`;
|
|
74
82
|
if (options.allowTests) logMessage += " allowTests=true";
|
|
75
83
|
if (options.contextLines) logMessage += ` contextLines=${options.contextLines}`;
|
|
76
84
|
if (options.format) logMessage += ` format=${options.format}`;
|
|
@@ -78,7 +86,12 @@ export async function extract(options) {
|
|
|
78
86
|
console.error(logMessage);
|
|
79
87
|
}
|
|
80
88
|
|
|
81
|
-
//
|
|
89
|
+
// If content is provided, use spawn with stdin piping
|
|
90
|
+
if (hasContent) {
|
|
91
|
+
return extractWithStdin(binaryPath, cliArgs, options.content, options);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Otherwise use exec for simple command execution
|
|
82
95
|
const command = `${binaryPath} extract ${cliArgs.join(' ')}`;
|
|
83
96
|
|
|
84
97
|
try {
|
|
@@ -88,62 +101,132 @@ export async function extract(options) {
|
|
|
88
101
|
console.error(`stderr: ${stderr}`);
|
|
89
102
|
}
|
|
90
103
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
// Calculate approximate request tokens
|
|
99
|
-
if (options.files && Array.isArray(options.files)) {
|
|
100
|
-
tokenUsage.requestTokens = options.files.join(' ').length / 4;
|
|
101
|
-
} else if (options.inputFile) {
|
|
102
|
-
tokenUsage.requestTokens = options.inputFile.length / 4;
|
|
103
|
-
}
|
|
104
|
+
return processExtractOutput(stdout, options);
|
|
105
|
+
} catch (error) {
|
|
106
|
+
// Enhance error message with command details
|
|
107
|
+
const errorMessage = `Error executing extract command: ${error.message}\nCommand: ${command}`;
|
|
108
|
+
throw new Error(errorMessage);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
104
111
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
112
|
+
/**
|
|
113
|
+
* Extract with content piped to stdin
|
|
114
|
+
* @private
|
|
115
|
+
*/
|
|
116
|
+
function extractWithStdin(binaryPath, cliArgs, content, options) {
|
|
117
|
+
return new Promise((resolve, reject) => {
|
|
118
|
+
const process = spawn(binaryPath, ['extract', ...cliArgs], {
|
|
119
|
+
stdio: ['pipe', 'pipe', 'pipe']
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
let stdout = '';
|
|
123
|
+
let stderr = '';
|
|
124
|
+
|
|
125
|
+
// Collect stdout
|
|
126
|
+
process.stdout.on('data', (data) => {
|
|
127
|
+
stdout += data.toString();
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
// Collect stderr
|
|
131
|
+
process.stderr.on('data', (data) => {
|
|
132
|
+
stderr += data.toString();
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
// Handle process exit
|
|
136
|
+
process.on('close', (code) => {
|
|
137
|
+
if (stderr && process.env.DEBUG === '1') {
|
|
138
|
+
console.error(`stderr: ${stderr}`);
|
|
111
139
|
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// Add token usage information to the output
|
|
115
|
-
let output = stdout;
|
|
116
140
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
141
|
+
if (code !== 0) {
|
|
142
|
+
reject(new Error(`Extract command failed with exit code ${code}: ${stderr}`));
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
121
145
|
|
|
122
|
-
// Parse JSON if requested or if format is json
|
|
123
|
-
if (options.json || options.format === 'json') {
|
|
124
146
|
try {
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
// Add token usage to JSON output
|
|
128
|
-
if (!jsonOutput.token_usage) {
|
|
129
|
-
jsonOutput.token_usage = {
|
|
130
|
-
request_tokens: tokenUsage.requestTokens,
|
|
131
|
-
response_tokens: tokenUsage.responseTokens,
|
|
132
|
-
total_tokens: tokenUsage.totalTokens
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
return jsonOutput;
|
|
147
|
+
const result = processExtractOutput(stdout, options);
|
|
148
|
+
resolve(result);
|
|
137
149
|
} catch (error) {
|
|
138
|
-
|
|
139
|
-
return output; // Fall back to string output with token usage
|
|
150
|
+
reject(error);
|
|
140
151
|
}
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
// Handle errors
|
|
155
|
+
process.on('error', (error) => {
|
|
156
|
+
reject(new Error(`Failed to spawn extract process: ${error.message}`));
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
// Write content to stdin and close
|
|
160
|
+
if (typeof content === 'string') {
|
|
161
|
+
process.stdin.write(content);
|
|
162
|
+
} else {
|
|
163
|
+
process.stdin.write(content);
|
|
141
164
|
}
|
|
165
|
+
process.stdin.end();
|
|
166
|
+
});
|
|
167
|
+
}
|
|
142
168
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
169
|
+
/**
|
|
170
|
+
* Process extract output and add token usage information
|
|
171
|
+
* @private
|
|
172
|
+
*/
|
|
173
|
+
function processExtractOutput(stdout, options) {
|
|
174
|
+
// Parse the output to extract token usage information
|
|
175
|
+
let tokenUsage = {
|
|
176
|
+
requestTokens: 0,
|
|
177
|
+
responseTokens: 0,
|
|
178
|
+
totalTokens: 0
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
// Calculate approximate request tokens
|
|
182
|
+
if (options.files && Array.isArray(options.files)) {
|
|
183
|
+
tokenUsage.requestTokens = options.files.join(' ').length / 4;
|
|
184
|
+
} else if (options.inputFile) {
|
|
185
|
+
tokenUsage.requestTokens = options.inputFile.length / 4;
|
|
186
|
+
} else if (options.content) {
|
|
187
|
+
const contentLength = typeof options.content === 'string'
|
|
188
|
+
? options.content.length
|
|
189
|
+
: options.content.byteLength;
|
|
190
|
+
tokenUsage.requestTokens = contentLength / 4;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Try to extract token information from the output
|
|
194
|
+
if (stdout.includes('Total tokens returned:')) {
|
|
195
|
+
const tokenMatch = stdout.match(/Total tokens returned: (\d+)/);
|
|
196
|
+
if (tokenMatch && tokenMatch[1]) {
|
|
197
|
+
tokenUsage.responseTokens = parseInt(tokenMatch[1], 10);
|
|
198
|
+
tokenUsage.totalTokens = tokenUsage.requestTokens + tokenUsage.responseTokens;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Add token usage information to the output
|
|
203
|
+
let output = stdout;
|
|
204
|
+
|
|
205
|
+
// Add token usage information at the end if not already present
|
|
206
|
+
if (!output.includes('Token Usage:')) {
|
|
207
|
+
output += `\nToken Usage:\n Request tokens: ${tokenUsage.requestTokens}\n Response tokens: ${tokenUsage.responseTokens}\n Total tokens: ${tokenUsage.totalTokens}\n`;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Parse JSON if requested or if format is json
|
|
211
|
+
if (options.json || options.format === 'json') {
|
|
212
|
+
try {
|
|
213
|
+
const jsonOutput = JSON.parse(stdout);
|
|
214
|
+
|
|
215
|
+
// Add token usage to JSON output
|
|
216
|
+
if (!jsonOutput.token_usage) {
|
|
217
|
+
jsonOutput.token_usage = {
|
|
218
|
+
request_tokens: tokenUsage.requestTokens,
|
|
219
|
+
response_tokens: tokenUsage.responseTokens,
|
|
220
|
+
total_tokens: tokenUsage.totalTokens
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return jsonOutput;
|
|
225
|
+
} catch (error) {
|
|
226
|
+
console.error('Error parsing JSON output:', error);
|
|
227
|
+
return output; // Fall back to string output with token usage
|
|
228
|
+
}
|
|
148
229
|
}
|
|
149
|
-
|
|
230
|
+
|
|
231
|
+
return output;
|
|
232
|
+
}
|