@probelabs/probe 0.6.0-rc173 → 0.6.0-rc175
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 +418 -281
- package/build/agent/probeTool.js +4 -13
- package/build/downloader.js +6 -2
- package/build/extract.js +13 -5
- package/build/extractor.js +6 -2
- 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 +12 -3
- package/build/tools/vercel.js +25 -29
- package/build/utils/file-lister.js +9 -2
- package/build/utils/path-validation.js +58 -0
- package/build/utils/symlink-utils.js +63 -0
- package/cjs/agent/ProbeAgent.cjs +515 -376
- package/cjs/index.cjs +535 -387
- package/index.d.ts +12 -1
- package/package.json +2 -2
- package/src/agent/ProbeAgent.d.ts +6 -0
- package/src/agent/ProbeAgent.js +69 -1
- package/src/agent/probeTool.js +4 -13
- package/src/downloader.js +6 -2
- package/src/extract.js +13 -5
- package/src/extractor.js +6 -2
- 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 +12 -3
- package/src/tools/vercel.js +25 -29
- package/src/utils/file-lister.js +9 -2
- package/src/utils/path-validation.js +58 -0
- package/src/utils/symlink-utils.js +63 -0
package/src/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,6 +49,7 @@ export const searchTool = (options = {}) => {
|
|
|
49
49
|
const searchOptions = {
|
|
50
50
|
query: searchQuery,
|
|
51
51
|
path: searchPath,
|
|
52
|
+
cwd: options.cwd, // Working directory for resolving relative paths
|
|
52
53
|
allowTests: allow_tests,
|
|
53
54
|
exact,
|
|
54
55
|
json: false,
|
|
@@ -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,6 +108,7 @@ 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
113
|
allow_tests,
|
|
112
114
|
json: false
|
|
@@ -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,6 +178,7 @@ export const extractTool = (options = {}) => {
|
|
|
184
178
|
// Set up extract options with input file
|
|
185
179
|
extractOptions = {
|
|
186
180
|
inputFile: tempFilePath,
|
|
181
|
+
cwd: effectiveCwd,
|
|
187
182
|
allowTests: allow_tests,
|
|
188
183
|
contextLines: context_lines,
|
|
189
184
|
format: effectiveFormat
|
|
@@ -202,6 +197,7 @@ export const extractTool = (options = {}) => {
|
|
|
202
197
|
// Set up extract options with files
|
|
203
198
|
extractOptions = {
|
|
204
199
|
files,
|
|
200
|
+
cwd: effectiveCwd,
|
|
205
201
|
allowTests: allow_tests,
|
|
206
202
|
contextLines: context_lines,
|
|
207
203
|
format: effectiveFormat
|
|
@@ -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 ? '...' : ''}"`);
|
package/src/utils/file-lister.js
CHANGED
|
@@ -7,6 +7,7 @@ import fs from 'fs';
|
|
|
7
7
|
import path from 'path';
|
|
8
8
|
import { promisify } from 'util';
|
|
9
9
|
import { exec } from 'child_process';
|
|
10
|
+
import { getEntryTypeSync } from './symlink-utils.js';
|
|
10
11
|
|
|
11
12
|
const execAsync = promisify(exec);
|
|
12
13
|
|
|
@@ -101,7 +102,10 @@ async function listFilesByLevelManually(directory, maxFiles, respectGitignore) {
|
|
|
101
102
|
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
102
103
|
|
|
103
104
|
// Process files first (at current level)
|
|
104
|
-
const files = entries.filter(entry =>
|
|
105
|
+
const files = entries.filter(entry => {
|
|
106
|
+
const fullPath = path.join(dir, entry.name);
|
|
107
|
+
return getEntryTypeSync(entry, fullPath).isFile;
|
|
108
|
+
});
|
|
105
109
|
for (const file of files) {
|
|
106
110
|
if (result.length >= maxFiles) break;
|
|
107
111
|
|
|
@@ -115,7 +119,10 @@ async function listFilesByLevelManually(directory, maxFiles, respectGitignore) {
|
|
|
115
119
|
}
|
|
116
120
|
|
|
117
121
|
// Then add directories to queue for next level
|
|
118
|
-
const dirs = entries.filter(entry =>
|
|
122
|
+
const dirs = entries.filter(entry => {
|
|
123
|
+
const fullPath = path.join(dir, entry.name);
|
|
124
|
+
return getEntryTypeSync(entry, fullPath).isDirectory;
|
|
125
|
+
});
|
|
119
126
|
for (const subdir of dirs) {
|
|
120
127
|
const subdirPath = path.join(dir, subdir.name);
|
|
121
128
|
const relativeSubdirPath = path.relative(directory, subdirPath);
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Symlink resolution utilities for the probe package
|
|
3
|
+
* @module utils/symlink-utils
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import fs from 'fs';
|
|
7
|
+
import { promises as fsPromises } from 'fs';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Get entry type following symlinks (async version)
|
|
11
|
+
*
|
|
12
|
+
* Uses fs.stat() which follows symlinks to get the actual target type.
|
|
13
|
+
* Falls back to dirent type if stat fails (e.g., broken symlink).
|
|
14
|
+
*
|
|
15
|
+
* @param {fs.Dirent} entry - Directory entry from readdir
|
|
16
|
+
* @param {string} fullPath - Full path to the entry
|
|
17
|
+
* @returns {Promise<{isFile: boolean, isDirectory: boolean, size: number}>}
|
|
18
|
+
*/
|
|
19
|
+
export async function getEntryType(entry, fullPath) {
|
|
20
|
+
try {
|
|
21
|
+
const stats = await fsPromises.stat(fullPath);
|
|
22
|
+
return {
|
|
23
|
+
isFile: stats.isFile(),
|
|
24
|
+
isDirectory: stats.isDirectory(),
|
|
25
|
+
size: stats.size
|
|
26
|
+
};
|
|
27
|
+
} catch {
|
|
28
|
+
// Fall back to dirent type if stat fails (e.g., broken symlink)
|
|
29
|
+
return {
|
|
30
|
+
isFile: entry.isFile(),
|
|
31
|
+
isDirectory: entry.isDirectory(),
|
|
32
|
+
size: 0
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Get entry type following symlinks (sync version)
|
|
39
|
+
*
|
|
40
|
+
* Uses fs.statSync() which follows symlinks to get the actual target type.
|
|
41
|
+
* Falls back to dirent type if stat fails (e.g., broken symlink).
|
|
42
|
+
*
|
|
43
|
+
* @param {fs.Dirent} entry - Directory entry from readdir
|
|
44
|
+
* @param {string} fullPath - Full path to the entry
|
|
45
|
+
* @returns {{isFile: boolean, isDirectory: boolean, size: number}}
|
|
46
|
+
*/
|
|
47
|
+
export function getEntryTypeSync(entry, fullPath) {
|
|
48
|
+
try {
|
|
49
|
+
const stats = fs.statSync(fullPath);
|
|
50
|
+
return {
|
|
51
|
+
isFile: stats.isFile(),
|
|
52
|
+
isDirectory: stats.isDirectory(),
|
|
53
|
+
size: stats.size
|
|
54
|
+
};
|
|
55
|
+
} catch {
|
|
56
|
+
// Fall back to dirent type if stat fails (e.g., broken symlink)
|
|
57
|
+
return {
|
|
58
|
+
isFile: entry.isFile(),
|
|
59
|
+
isDirectory: entry.isDirectory(),
|
|
60
|
+
size: 0
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
}
|