@probelabs/probe 0.6.0-rc174 → 0.6.0-rc176
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.d.ts +6 -0
- package/build/agent/ProbeAgent.js +69 -1
- package/build/agent/index.js +332 -233
- package/build/agent/simpleTelemetry.js +13 -0
- package/build/extract.js +13 -5
- package/build/query.js +9 -2
- package/build/search.js +10 -2
- package/build/tools/bash.js +5 -5
- package/build/tools/edit.js +7 -7
- package/build/tools/langchain.js +15 -6
- package/build/tools/vercel.js +29 -33
- package/build/utils/path-validation.js +58 -0
- package/cjs/agent/ProbeAgent.cjs +453 -364
- package/cjs/agent/simpleTelemetry.cjs +11 -0
- package/cjs/index.cjs +487 -378
- package/index.d.ts +12 -1
- package/package.json +1 -1
- package/src/agent/ProbeAgent.d.ts +6 -0
- package/src/agent/ProbeAgent.js +69 -1
- package/src/agent/simpleTelemetry.js +13 -0
- package/src/extract.js +13 -5
- package/src/query.js +9 -2
- package/src/search.js +10 -2
- package/src/tools/bash.js +5 -5
- package/src/tools/edit.js +7 -7
- package/src/tools/langchain.js +15 -6
- package/src/tools/vercel.js +29 -33
- package/src/utils/path-validation.js +58 -0
|
@@ -170,6 +170,19 @@ export class SimpleAppTracer {
|
|
|
170
170
|
}
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
+
/**
|
|
174
|
+
* Record a generic event (used by completionPrompt and other features)
|
|
175
|
+
*/
|
|
176
|
+
// visor-disable: SimpleAppTracer uses this.sessionId because it's a per-session instance. AppTracer extracts from attributes because it's a singleton managing multiple sessions. Different architectures require different approaches.
|
|
177
|
+
recordEvent(name, attributes = {}) {
|
|
178
|
+
if (!this.isEnabled()) return;
|
|
179
|
+
|
|
180
|
+
this.addEvent(name, {
|
|
181
|
+
'session.id': this.sessionId,
|
|
182
|
+
...attributes
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
|
|
173
186
|
/**
|
|
174
187
|
* Record delegation events
|
|
175
188
|
*/
|
package/build/extract.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import { exec, spawn } from 'child_process';
|
|
7
7
|
import { promisify } from 'util';
|
|
8
8
|
import { getBinaryPath, buildCliArgs, escapeString } from './utils.js';
|
|
9
|
+
import { validateCwdPath } from './utils/path-validation.js';
|
|
9
10
|
|
|
10
11
|
const execAsync = promisify(exec);
|
|
11
12
|
|
|
@@ -27,6 +28,7 @@ const EXTRACT_FLAG_MAP = {
|
|
|
27
28
|
* @param {string[]} [options.files] - Files to extract from (can include line numbers with colon, e.g., "/path/to/file.rs:10")
|
|
28
29
|
* @param {string} [options.inputFile] - Path to a file containing unstructured text to extract file paths from
|
|
29
30
|
* @param {string|Buffer} [options.content] - Content to pipe to stdin (e.g., git diff output). Alternative to inputFile.
|
|
31
|
+
* @param {string} [options.cwd] - Working directory for resolving relative file paths
|
|
30
32
|
* @param {boolean} [options.allowTests] - Include test files
|
|
31
33
|
* @param {number} [options.contextLines] - Number of context lines to include
|
|
32
34
|
* @param {string} [options.format] - Output format ('markdown', 'plain', 'json', 'xml', 'color', 'outline-xml', 'outline-diff')
|
|
@@ -71,6 +73,10 @@ export async function extract(options) {
|
|
|
71
73
|
}
|
|
72
74
|
}
|
|
73
75
|
|
|
76
|
+
// Get the working directory (cwd option sets working directory for relative file resolution)
|
|
77
|
+
// Validate and normalize the path to prevent path traversal attacks
|
|
78
|
+
const cwd = await validateCwdPath(options.cwd);
|
|
79
|
+
|
|
74
80
|
// Create a single log record with all extract parameters (only in debug mode)
|
|
75
81
|
if (process.env.DEBUG === '1') {
|
|
76
82
|
let logMessage = `\nExtract:`;
|
|
@@ -79,6 +85,7 @@ export async function extract(options) {
|
|
|
79
85
|
}
|
|
80
86
|
if (options.inputFile) logMessage += ` inputFile="${options.inputFile}"`;
|
|
81
87
|
if (options.content) logMessage += ` content=(${typeof options.content === 'string' ? options.content.length : options.content.byteLength} bytes)`;
|
|
88
|
+
if (options.cwd) logMessage += ` cwd="${cwd}"`;
|
|
82
89
|
if (options.allowTests) logMessage += " allowTests=true";
|
|
83
90
|
if (options.contextLines) logMessage += ` contextLines=${options.contextLines}`;
|
|
84
91
|
if (options.format) logMessage += ` format=${options.format}`;
|
|
@@ -88,14 +95,14 @@ export async function extract(options) {
|
|
|
88
95
|
|
|
89
96
|
// If content is provided, use spawn with stdin piping
|
|
90
97
|
if (hasContent) {
|
|
91
|
-
return extractWithStdin(binaryPath, cliArgs, options.content, options);
|
|
98
|
+
return extractWithStdin(binaryPath, cliArgs, options.content, options, cwd);
|
|
92
99
|
}
|
|
93
100
|
|
|
94
101
|
// Otherwise use exec for simple command execution
|
|
95
102
|
const command = `${binaryPath} extract ${cliArgs.join(' ')}`;
|
|
96
103
|
|
|
97
104
|
try {
|
|
98
|
-
const { stdout, stderr } = await execAsync(command);
|
|
105
|
+
const { stdout, stderr } = await execAsync(command, { cwd });
|
|
99
106
|
|
|
100
107
|
if (stderr) {
|
|
101
108
|
console.error(`stderr: ${stderr}`);
|
|
@@ -104,7 +111,7 @@ export async function extract(options) {
|
|
|
104
111
|
return processExtractOutput(stdout, options);
|
|
105
112
|
} catch (error) {
|
|
106
113
|
// Enhance error message with command details
|
|
107
|
-
const errorMessage = `Error executing extract command: ${error.message}\nCommand: ${command}`;
|
|
114
|
+
const errorMessage = `Error executing extract command: ${error.message}\nCommand: ${command}\nCwd: ${cwd}`;
|
|
108
115
|
throw new Error(errorMessage);
|
|
109
116
|
}
|
|
110
117
|
}
|
|
@@ -113,10 +120,11 @@ export async function extract(options) {
|
|
|
113
120
|
* Extract with content piped to stdin
|
|
114
121
|
* @private
|
|
115
122
|
*/
|
|
116
|
-
function extractWithStdin(binaryPath, cliArgs, content, options) {
|
|
123
|
+
function extractWithStdin(binaryPath, cliArgs, content, options, cwd) {
|
|
117
124
|
return new Promise((resolve, reject) => {
|
|
118
125
|
const childProcess = spawn(binaryPath, ['extract', ...cliArgs], {
|
|
119
|
-
stdio: ['pipe', 'pipe', 'pipe']
|
|
126
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
127
|
+
cwd
|
|
120
128
|
});
|
|
121
129
|
|
|
122
130
|
let stdout = '';
|
package/build/query.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import { exec } from 'child_process';
|
|
7
7
|
import { promisify } from 'util';
|
|
8
8
|
import { getBinaryPath, buildCliArgs, escapeString } from './utils.js';
|
|
9
|
+
import { validateCwdPath } from './utils/path-validation.js';
|
|
9
10
|
|
|
10
11
|
const execAsync = promisify(exec);
|
|
11
12
|
|
|
@@ -26,6 +27,7 @@ const QUERY_FLAG_MAP = {
|
|
|
26
27
|
*
|
|
27
28
|
* @param {Object} options - Query options
|
|
28
29
|
* @param {string} options.path - Path to search in
|
|
30
|
+
* @param {string} [options.cwd] - Working directory for resolving relative paths (defaults to process.cwd())
|
|
29
31
|
* @param {string} options.pattern - The ast-grep pattern to search for
|
|
30
32
|
* @param {string} [options.language] - Programming language to search in
|
|
31
33
|
* @param {string[]} [options.ignore] - Patterns to ignore
|
|
@@ -62,9 +64,14 @@ export async function query(options) {
|
|
|
62
64
|
// Add pattern and path as positional arguments
|
|
63
65
|
cliArgs.push(escapeString(options.pattern), escapeString(options.path));
|
|
64
66
|
|
|
67
|
+
// Get the working directory (cwd option for resolving relative paths)
|
|
68
|
+
// Validate and normalize the path to prevent path traversal attacks
|
|
69
|
+
const cwd = await validateCwdPath(options.cwd);
|
|
70
|
+
|
|
65
71
|
// Create a single log record with all query parameters (only in debug mode)
|
|
66
72
|
if (process.env.DEBUG === '1') {
|
|
67
73
|
let logMessage = `Query: pattern="${options.pattern}" path="${options.path}"`;
|
|
74
|
+
if (options.cwd) logMessage += ` cwd="${options.cwd}"`;
|
|
68
75
|
if (options.language) logMessage += ` language=${options.language}`;
|
|
69
76
|
if (options.maxResults) logMessage += ` maxResults=${options.maxResults}`;
|
|
70
77
|
if (options.allowTests) logMessage += " allowTests=true";
|
|
@@ -75,7 +82,7 @@ export async function query(options) {
|
|
|
75
82
|
const command = `${binaryPath} query ${cliArgs.join(' ')}`;
|
|
76
83
|
|
|
77
84
|
try {
|
|
78
|
-
const { stdout, stderr } = await execAsync(command);
|
|
85
|
+
const { stdout, stderr } = await execAsync(command, { cwd });
|
|
79
86
|
|
|
80
87
|
if (stderr) {
|
|
81
88
|
console.error(`stderr: ${stderr}`);
|
|
@@ -110,7 +117,7 @@ export async function query(options) {
|
|
|
110
117
|
return stdout;
|
|
111
118
|
} catch (error) {
|
|
112
119
|
// Enhance error message with command details
|
|
113
|
-
const errorMessage = `Error executing query command: ${error.message}\nCommand: ${command}`;
|
|
120
|
+
const errorMessage = `Error executing query command: ${error.message}\nCommand: ${command}\nCwd: ${cwd}`;
|
|
114
121
|
throw new Error(errorMessage);
|
|
115
122
|
}
|
|
116
123
|
}
|
package/build/search.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import { execFile } from 'child_process';
|
|
7
7
|
import { promisify } from 'util';
|
|
8
8
|
import { getBinaryPath, buildCliArgs } from './utils.js';
|
|
9
|
+
import { validateCwdPath } from './utils/path-validation.js';
|
|
9
10
|
|
|
10
11
|
const execFileAsync = promisify(execFile);
|
|
11
12
|
|
|
@@ -38,6 +39,7 @@ const SEARCH_FLAG_MAP = {
|
|
|
38
39
|
*
|
|
39
40
|
* @param {Object} options - Search options
|
|
40
41
|
* @param {string} options.path - Path to search in
|
|
42
|
+
* @param {string} [options.cwd] - Working directory for resolving relative paths (defaults to process.cwd())
|
|
41
43
|
* @param {string|string[]} options.query - Search query or queries
|
|
42
44
|
* @param {boolean} [options.filesOnly] - Only output file paths
|
|
43
45
|
* @param {string[]} [options.ignore] - Patterns to ignore
|
|
@@ -125,9 +127,14 @@ export async function search(options) {
|
|
|
125
127
|
// Add query and path as positional arguments
|
|
126
128
|
const queries = Array.isArray(options.query) ? options.query : [options.query];
|
|
127
129
|
|
|
130
|
+
// Get the working directory (cwd option for resolving relative paths)
|
|
131
|
+
// Validate and normalize the path to prevent path traversal attacks
|
|
132
|
+
const cwd = await validateCwdPath(options.cwd);
|
|
133
|
+
|
|
128
134
|
// Create a single log record with all search parameters (only in debug mode)
|
|
129
135
|
if (process.env.DEBUG === '1') {
|
|
130
136
|
let logMessage = `\nSearch: query="${queries[0]}" path="${options.path}"`;
|
|
137
|
+
if (options.cwd) logMessage += ` cwd="${options.cwd}"`;
|
|
131
138
|
if (options.maxResults) logMessage += ` maxResults=${options.maxResults}`;
|
|
132
139
|
logMessage += ` maxTokens=${options.maxTokens}`;
|
|
133
140
|
logMessage += ` timeout=${options.timeout}`;
|
|
@@ -154,6 +161,7 @@ export async function search(options) {
|
|
|
154
161
|
try {
|
|
155
162
|
// Execute with execFile (no shell, prevents command injection)
|
|
156
163
|
const { stdout, stderr } = await execFileAsync(binaryPath, args, {
|
|
164
|
+
cwd,
|
|
157
165
|
timeout: options.timeout * 1000, // Convert seconds to milliseconds
|
|
158
166
|
maxBuffer: 50 * 1024 * 1024 // 50MB buffer for large outputs
|
|
159
167
|
});
|
|
@@ -226,13 +234,13 @@ export async function search(options) {
|
|
|
226
234
|
} catch (error) {
|
|
227
235
|
// Check if the error is a timeout
|
|
228
236
|
if (error.code === 'ETIMEDOUT' || error.killed) {
|
|
229
|
-
const timeoutMessage = `Search operation timed out after ${options.timeout} seconds.\nBinary: ${binaryPath}\nArgs: ${args.join(' ')}`;
|
|
237
|
+
const timeoutMessage = `Search operation timed out after ${options.timeout} seconds.\nBinary: ${binaryPath}\nArgs: ${args.join(' ')}\nCwd: ${cwd}`;
|
|
230
238
|
console.error(timeoutMessage);
|
|
231
239
|
throw new Error(timeoutMessage);
|
|
232
240
|
}
|
|
233
241
|
|
|
234
242
|
// Enhance error message with command details
|
|
235
|
-
const errorMessage = `Error executing search command: ${error.message}\nBinary: ${binaryPath}\nArgs: ${args.join(' ')}`;
|
|
243
|
+
const errorMessage = `Error executing search command: ${error.message}\nBinary: ${binaryPath}\nArgs: ${args.join(' ')}\nCwd: ${cwd}`;
|
|
236
244
|
throw new Error(errorMessage);
|
|
237
245
|
}
|
|
238
246
|
}
|
package/build/tools/bash.js
CHANGED
|
@@ -22,15 +22,15 @@ import { executeBashCommand, formatExecutionResult, validateExecutionOptions } f
|
|
|
22
22
|
* @param {Object} [options.bashConfig.env={}] - Default environment variables
|
|
23
23
|
* @param {number} [options.bashConfig.maxBuffer] - Maximum output buffer size
|
|
24
24
|
* @param {boolean} [options.debug=false] - Enable debug logging
|
|
25
|
-
* @param {string} [options.
|
|
25
|
+
* @param {string} [options.cwd] - Working directory from probe config
|
|
26
26
|
* @param {string[]} [options.allowedFolders] - Allowed directories for execution
|
|
27
27
|
* @returns {Object} Configured bash tool
|
|
28
28
|
*/
|
|
29
29
|
export const bashTool = (options = {}) => {
|
|
30
|
-
const {
|
|
30
|
+
const {
|
|
31
31
|
bashConfig = {},
|
|
32
32
|
debug = false,
|
|
33
|
-
|
|
33
|
+
cwd,
|
|
34
34
|
allowedFolders = []
|
|
35
35
|
} = options;
|
|
36
36
|
|
|
@@ -48,8 +48,8 @@ export const bashTool = (options = {}) => {
|
|
|
48
48
|
if (bashConfig.workingDirectory) {
|
|
49
49
|
return bashConfig.workingDirectory;
|
|
50
50
|
}
|
|
51
|
-
if (
|
|
52
|
-
return
|
|
51
|
+
if (cwd) {
|
|
52
|
+
return cwd;
|
|
53
53
|
}
|
|
54
54
|
if (allowedFolders && allowedFolders.length > 0) {
|
|
55
55
|
return allowedFolders[0];
|
package/build/tools/edit.js
CHANGED
|
@@ -40,7 +40,7 @@ function parseFileToolOptions(options = {}) {
|
|
|
40
40
|
return {
|
|
41
41
|
debug: options.debug || false,
|
|
42
42
|
allowedFolders: options.allowedFolders || [],
|
|
43
|
-
|
|
43
|
+
cwd: options.cwd
|
|
44
44
|
};
|
|
45
45
|
}
|
|
46
46
|
|
|
@@ -50,11 +50,11 @@ function parseFileToolOptions(options = {}) {
|
|
|
50
50
|
* @param {Object} [options] - Configuration options
|
|
51
51
|
* @param {boolean} [options.debug=false] - Enable debug logging
|
|
52
52
|
* @param {string[]} [options.allowedFolders] - Allowed directories for file operations
|
|
53
|
-
* @param {string} [options.
|
|
53
|
+
* @param {string} [options.cwd] - Working directory
|
|
54
54
|
* @returns {Object} Configured edit tool
|
|
55
55
|
*/
|
|
56
56
|
export const editTool = (options = {}) => {
|
|
57
|
-
const { debug, allowedFolders,
|
|
57
|
+
const { debug, allowedFolders, cwd } = parseFileToolOptions(options);
|
|
58
58
|
|
|
59
59
|
return tool({
|
|
60
60
|
name: 'edit',
|
|
@@ -111,7 +111,7 @@ Important:
|
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
// Resolve the file path
|
|
114
|
-
const resolvedPath = isAbsolute(file_path) ? file_path : resolve(
|
|
114
|
+
const resolvedPath = isAbsolute(file_path) ? file_path : resolve(cwd || process.cwd(), file_path);
|
|
115
115
|
|
|
116
116
|
if (debug) {
|
|
117
117
|
console.error(`[Edit] Attempting to edit file: ${resolvedPath}`);
|
|
@@ -182,11 +182,11 @@ Important:
|
|
|
182
182
|
* @param {Object} [options] - Configuration options
|
|
183
183
|
* @param {boolean} [options.debug=false] - Enable debug logging
|
|
184
184
|
* @param {string[]} [options.allowedFolders] - Allowed directories for file operations
|
|
185
|
-
* @param {string} [options.
|
|
185
|
+
* @param {string} [options.cwd] - Working directory
|
|
186
186
|
* @returns {Object} Configured create tool
|
|
187
187
|
*/
|
|
188
188
|
export const createTool = (options = {}) => {
|
|
189
|
-
const { debug, allowedFolders,
|
|
189
|
+
const { debug, allowedFolders, cwd } = parseFileToolOptions(options);
|
|
190
190
|
|
|
191
191
|
return tool({
|
|
192
192
|
name: 'create',
|
|
@@ -235,7 +235,7 @@ Important:
|
|
|
235
235
|
}
|
|
236
236
|
|
|
237
237
|
// Resolve the file path
|
|
238
|
-
const resolvedPath = isAbsolute(file_path) ? file_path : resolve(
|
|
238
|
+
const resolvedPath = isAbsolute(file_path) ? file_path : resolve(cwd || process.cwd(), file_path);
|
|
239
239
|
|
|
240
240
|
if (debug) {
|
|
241
241
|
console.error(`[Create] Attempting to create file: ${resolvedPath}`);
|
package/build/tools/langchain.js
CHANGED
|
@@ -9,7 +9,9 @@ import { extract } from '../extract.js';
|
|
|
9
9
|
import { searchSchema, querySchema, extractSchema, searchDescription, queryDescription, extractDescription, parseTargets } from './common.js';
|
|
10
10
|
|
|
11
11
|
// LangChain tool for searching code
|
|
12
|
-
export function createSearchTool() {
|
|
12
|
+
export function createSearchTool(options = {}) {
|
|
13
|
+
const { cwd } = options;
|
|
14
|
+
|
|
13
15
|
return {
|
|
14
16
|
name: 'search',
|
|
15
17
|
description: searchDescription,
|
|
@@ -19,7 +21,8 @@ export function createSearchTool() {
|
|
|
19
21
|
const results = await search({
|
|
20
22
|
query: searchQuery,
|
|
21
23
|
path,
|
|
22
|
-
|
|
24
|
+
cwd, // Working directory for resolving relative paths
|
|
25
|
+
allowTests: allow_tests ?? true,
|
|
23
26
|
exact,
|
|
24
27
|
json: false,
|
|
25
28
|
maxResults,
|
|
@@ -37,7 +40,9 @@ export function createSearchTool() {
|
|
|
37
40
|
}
|
|
38
41
|
|
|
39
42
|
// LangChain tool for querying code
|
|
40
|
-
export function createQueryTool() {
|
|
43
|
+
export function createQueryTool(options = {}) {
|
|
44
|
+
const { cwd } = options;
|
|
45
|
+
|
|
41
46
|
return {
|
|
42
47
|
name: 'query',
|
|
43
48
|
description: queryDescription,
|
|
@@ -47,8 +52,9 @@ export function createQueryTool() {
|
|
|
47
52
|
const results = await query({
|
|
48
53
|
pattern,
|
|
49
54
|
path,
|
|
55
|
+
cwd, // Working directory for resolving relative paths
|
|
50
56
|
language,
|
|
51
|
-
allow_tests,
|
|
57
|
+
allowTests: allow_tests ?? true,
|
|
52
58
|
json: false
|
|
53
59
|
});
|
|
54
60
|
|
|
@@ -62,7 +68,9 @@ export function createQueryTool() {
|
|
|
62
68
|
}
|
|
63
69
|
|
|
64
70
|
// LangChain tool for extracting code
|
|
65
|
-
export function createExtractTool() {
|
|
71
|
+
export function createExtractTool(options = {}) {
|
|
72
|
+
const { cwd } = options;
|
|
73
|
+
|
|
66
74
|
return {
|
|
67
75
|
name: 'extract',
|
|
68
76
|
description: extractDescription,
|
|
@@ -74,7 +82,8 @@ export function createExtractTool() {
|
|
|
74
82
|
|
|
75
83
|
const results = await extract({
|
|
76
84
|
files,
|
|
77
|
-
|
|
85
|
+
cwd, // Working directory for resolving relative paths
|
|
86
|
+
allowTests: allow_tests ?? true,
|
|
78
87
|
contextLines: context_lines,
|
|
79
88
|
format
|
|
80
89
|
});
|
package/build/tools/vercel.js
CHANGED
|
@@ -31,15 +31,15 @@ export const searchTool = (options = {}) => {
|
|
|
31
31
|
// Use parameter maxTokens if provided, otherwise use the default
|
|
32
32
|
const effectiveMaxTokens = paramMaxTokens || maxTokens;
|
|
33
33
|
|
|
34
|
-
// Use the path from parameters if provided, otherwise use
|
|
35
|
-
let searchPath = path || options.
|
|
34
|
+
// Use the path from parameters if provided, otherwise use cwd from config
|
|
35
|
+
let searchPath = path || options.cwd || '.';
|
|
36
36
|
|
|
37
|
-
// If path is "." or "./", use the
|
|
38
|
-
if ((searchPath === "." || searchPath === "./") && options.
|
|
37
|
+
// If path is "." or "./", use the cwd if available
|
|
38
|
+
if ((searchPath === "." || searchPath === "./") && options.cwd) {
|
|
39
39
|
if (debug) {
|
|
40
|
-
console.error(`Using
|
|
40
|
+
console.error(`Using cwd "${options.cwd}" instead of "${searchPath}"`);
|
|
41
41
|
}
|
|
42
|
-
searchPath = options.
|
|
42
|
+
searchPath = options.cwd;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
if (debug) {
|
|
@@ -49,7 +49,8 @@ export const searchTool = (options = {}) => {
|
|
|
49
49
|
const searchOptions = {
|
|
50
50
|
query: searchQuery,
|
|
51
51
|
path: searchPath,
|
|
52
|
-
|
|
52
|
+
cwd: options.cwd, // Working directory for resolving relative paths
|
|
53
|
+
allowTests: allow_tests ?? true,
|
|
53
54
|
exact,
|
|
54
55
|
json: false,
|
|
55
56
|
maxTokens: effectiveMaxTokens,
|
|
@@ -89,15 +90,15 @@ export const queryTool = (options = {}) => {
|
|
|
89
90
|
inputSchema: querySchema,
|
|
90
91
|
execute: async ({ pattern, path, language, allow_tests }) => {
|
|
91
92
|
try {
|
|
92
|
-
// Use the path from parameters if provided, otherwise use
|
|
93
|
-
let queryPath = path || options.
|
|
93
|
+
// Use the path from parameters if provided, otherwise use cwd from config
|
|
94
|
+
let queryPath = path || options.cwd || '.';
|
|
94
95
|
|
|
95
|
-
// If path is "." or "./", use the
|
|
96
|
-
if ((queryPath === "." || queryPath === "./") && options.
|
|
96
|
+
// If path is "." or "./", use the cwd if available
|
|
97
|
+
if ((queryPath === "." || queryPath === "./") && options.cwd) {
|
|
97
98
|
if (debug) {
|
|
98
|
-
console.error(`Using
|
|
99
|
+
console.error(`Using cwd "${options.cwd}" instead of "${queryPath}"`);
|
|
99
100
|
}
|
|
100
|
-
queryPath = options.
|
|
101
|
+
queryPath = options.cwd;
|
|
101
102
|
}
|
|
102
103
|
|
|
103
104
|
if (debug) {
|
|
@@ -107,8 +108,9 @@ export const queryTool = (options = {}) => {
|
|
|
107
108
|
const results = await query({
|
|
108
109
|
pattern,
|
|
109
110
|
path: queryPath,
|
|
111
|
+
cwd: options.cwd, // Working directory for resolving relative paths
|
|
110
112
|
language,
|
|
111
|
-
allow_tests,
|
|
113
|
+
allowTests: allow_tests ?? true,
|
|
112
114
|
json: false
|
|
113
115
|
});
|
|
114
116
|
|
|
@@ -137,28 +139,20 @@ export const extractTool = (options = {}) => {
|
|
|
137
139
|
inputSchema: extractSchema,
|
|
138
140
|
execute: async ({ targets, input_content, line, end_line, allow_tests, context_lines, format }) => {
|
|
139
141
|
try {
|
|
140
|
-
// Use the
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
// If path is "." or "./", use the defaultPath if available
|
|
144
|
-
if ((extractPath === "." || extractPath === "./") && options.defaultPath) {
|
|
145
|
-
if (debug) {
|
|
146
|
-
console.error(`Using default path "${options.defaultPath}" instead of "${extractPath}"`);
|
|
147
|
-
}
|
|
148
|
-
extractPath = options.defaultPath;
|
|
149
|
-
}
|
|
142
|
+
// Use the cwd from config for working directory
|
|
143
|
+
const effectiveCwd = options.cwd || '.';
|
|
150
144
|
|
|
151
145
|
if (debug) {
|
|
152
146
|
if (targets) {
|
|
153
|
-
console.error(`Executing extract with targets: "${targets}",
|
|
147
|
+
console.error(`Executing extract with targets: "${targets}", cwd: "${effectiveCwd}", context lines: ${context_lines || 10}`);
|
|
154
148
|
} else if (input_content) {
|
|
155
|
-
console.error(`Executing extract with input content,
|
|
149
|
+
console.error(`Executing extract with input content, cwd: "${effectiveCwd}", context lines: ${context_lines || 10}`);
|
|
156
150
|
}
|
|
157
151
|
}
|
|
158
152
|
|
|
159
153
|
// Create a temporary file for input content if provided
|
|
160
154
|
let tempFilePath = null;
|
|
161
|
-
let extractOptions = {
|
|
155
|
+
let extractOptions = { cwd: effectiveCwd };
|
|
162
156
|
|
|
163
157
|
if (input_content) {
|
|
164
158
|
// Import required modules
|
|
@@ -184,7 +178,8 @@ export const extractTool = (options = {}) => {
|
|
|
184
178
|
// Set up extract options with input file
|
|
185
179
|
extractOptions = {
|
|
186
180
|
inputFile: tempFilePath,
|
|
187
|
-
|
|
181
|
+
cwd: effectiveCwd,
|
|
182
|
+
allowTests: allow_tests ?? true,
|
|
188
183
|
contextLines: context_lines,
|
|
189
184
|
format: effectiveFormat
|
|
190
185
|
};
|
|
@@ -202,7 +197,8 @@ export const extractTool = (options = {}) => {
|
|
|
202
197
|
// Set up extract options with files
|
|
203
198
|
extractOptions = {
|
|
204
199
|
files,
|
|
205
|
-
|
|
200
|
+
cwd: effectiveCwd,
|
|
201
|
+
allowTests: allow_tests ?? true,
|
|
206
202
|
contextLines: context_lines,
|
|
207
203
|
format: effectiveFormat
|
|
208
204
|
};
|
|
@@ -241,12 +237,12 @@ export const extractTool = (options = {}) => {
|
|
|
241
237
|
* @param {Object} [options] - Configuration options
|
|
242
238
|
* @param {boolean} [options.debug=false] - Enable debug logging
|
|
243
239
|
* @param {number} [options.timeout=300] - Default timeout in seconds
|
|
244
|
-
* @param {string} [options.
|
|
240
|
+
* @param {string} [options.cwd] - Working directory to use if not specified in call
|
|
245
241
|
* @param {string[]} [options.allowedFolders] - Allowed folders for workspace isolation
|
|
246
242
|
* @returns {Object} Configured delegate tool
|
|
247
243
|
*/
|
|
248
244
|
export const delegateTool = (options = {}) => {
|
|
249
|
-
const { debug = false, timeout = 300,
|
|
245
|
+
const { debug = false, timeout = 300, cwd, allowedFolders } = options;
|
|
250
246
|
|
|
251
247
|
return tool({
|
|
252
248
|
name: 'delegate',
|
|
@@ -289,8 +285,8 @@ export const delegateTool = (options = {}) => {
|
|
|
289
285
|
}
|
|
290
286
|
|
|
291
287
|
// Use inherited path if not specified in AI call
|
|
292
|
-
// Priority: explicit path >
|
|
293
|
-
const effectivePath = path ||
|
|
288
|
+
// Priority: explicit path > cwd > first allowedFolder
|
|
289
|
+
const effectivePath = path || cwd || (allowedFolders && allowedFolders[0]);
|
|
294
290
|
|
|
295
291
|
if (debug) {
|
|
296
292
|
console.error(`Executing delegate with task: "${task.substring(0, 100)}${task.length > 100 ? '...' : ''}"`);
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Path validation utilities for the probe package
|
|
3
|
+
* @module utils/path-validation
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import path from 'path';
|
|
7
|
+
import { promises as fs } from 'fs';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Validates and normalizes a path to be used as working directory (cwd).
|
|
11
|
+
*
|
|
12
|
+
* Security considerations:
|
|
13
|
+
* - Normalizes path to resolve '..' and '.' components
|
|
14
|
+
* - Returns absolute path to prevent ambiguity
|
|
15
|
+
* - Does NOT restrict access to specific directories (that's the responsibility
|
|
16
|
+
* of higher-level components like ProbeAgent with allowedFolders)
|
|
17
|
+
*
|
|
18
|
+
* @param {string} inputPath - The path to validate
|
|
19
|
+
* @param {string} [defaultPath] - Default path to use if inputPath is not provided
|
|
20
|
+
* @returns {Promise<string>} Normalized absolute path
|
|
21
|
+
* @throws {Error} If the path is invalid or doesn't exist
|
|
22
|
+
*/
|
|
23
|
+
export async function validateCwdPath(inputPath, defaultPath = process.cwd()) {
|
|
24
|
+
// Use default if not provided
|
|
25
|
+
const targetPath = inputPath || defaultPath;
|
|
26
|
+
|
|
27
|
+
// Normalize and resolve to absolute path
|
|
28
|
+
// This handles '..' traversal and makes the path unambiguous
|
|
29
|
+
const normalizedPath = path.normalize(path.resolve(targetPath));
|
|
30
|
+
|
|
31
|
+
// Verify the path exists and is a directory
|
|
32
|
+
try {
|
|
33
|
+
const stats = await fs.stat(normalizedPath);
|
|
34
|
+
if (!stats.isDirectory()) {
|
|
35
|
+
throw new Error(`Path is not a directory: ${normalizedPath}`);
|
|
36
|
+
}
|
|
37
|
+
} catch (error) {
|
|
38
|
+
if (error.code === 'ENOENT') {
|
|
39
|
+
throw new Error(`Path does not exist: ${normalizedPath}`);
|
|
40
|
+
}
|
|
41
|
+
throw error;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return normalizedPath;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Validates a path option without requiring it to exist.
|
|
49
|
+
* Use this for paths that might be created or are optional.
|
|
50
|
+
*
|
|
51
|
+
* @param {string} inputPath - The path to validate
|
|
52
|
+
* @param {string} [defaultPath] - Default path to use if inputPath is not provided
|
|
53
|
+
* @returns {string} Normalized absolute path
|
|
54
|
+
*/
|
|
55
|
+
export function normalizePath(inputPath, defaultPath = process.cwd()) {
|
|
56
|
+
const targetPath = inputPath || defaultPath;
|
|
57
|
+
return path.normalize(path.resolve(targetPath));
|
|
58
|
+
}
|