@probelabs/probe 0.6.0-rc146 → 0.6.0-rc148

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.
@@ -0,0 +1,271 @@
1
+ /**
2
+ * Binary extractor for bundled probe binaries
3
+ * @module extractor
4
+ */
5
+
6
+ import fs from 'fs-extra';
7
+ import path from 'path';
8
+ import tar from 'tar';
9
+ import AdmZip from 'adm-zip';
10
+ import os from 'os';
11
+ import { fileURLToPath } from 'url';
12
+
13
+ const __filename = fileURLToPath(import.meta.url);
14
+ const __dirname = path.dirname(__filename);
15
+
16
+ const BINARY_NAME = "probe";
17
+
18
+ /**
19
+ * Detects the current OS and architecture
20
+ * @returns {Object} Object containing OS and architecture information
21
+ */
22
+ function detectPlatform() {
23
+ const osType = os.platform();
24
+ const archType = os.arch();
25
+
26
+ let platform;
27
+ let extension;
28
+
29
+ // Map to the same format used in release artifacts
30
+ if (osType === 'linux') {
31
+ if (archType === 'x64') {
32
+ platform = 'x86_64-unknown-linux-gnu';
33
+ extension = 'tar.gz';
34
+ } else if (archType === 'arm64') {
35
+ platform = 'aarch64-unknown-linux-gnu';
36
+ extension = 'tar.gz';
37
+ } else {
38
+ throw new Error(`Unsupported Linux architecture: ${archType}`);
39
+ }
40
+ } else if (osType === 'darwin') {
41
+ if (archType === 'x64') {
42
+ platform = 'x86_64-apple-darwin';
43
+ extension = 'tar.gz';
44
+ } else if (archType === 'arm64') {
45
+ platform = 'aarch64-apple-darwin';
46
+ extension = 'tar.gz';
47
+ } else {
48
+ throw new Error(`Unsupported macOS architecture: ${archType}`);
49
+ }
50
+ } else if (osType === 'win32') {
51
+ if (archType === 'x64') {
52
+ platform = 'x86_64-pc-windows-msvc';
53
+ extension = 'zip';
54
+ } else {
55
+ throw new Error(`Unsupported Windows architecture: ${archType}`);
56
+ }
57
+ } else {
58
+ throw new Error(`Unsupported operating system: ${osType}`);
59
+ }
60
+
61
+ if (process.env.DEBUG === '1' || process.env.VERBOSE === '1') {
62
+ console.log(`Detected platform: ${platform}`);
63
+ }
64
+
65
+ return { platform, extension, osType, archType };
66
+ }
67
+
68
+ /**
69
+ * Validates that a path is within a base directory (prevents path traversal)
70
+ * @param {string} filePath - Path to validate
71
+ * @param {string} baseDir - Base directory that filePath must be within
72
+ * @returns {boolean} True if path is safe
73
+ */
74
+ function isPathSafe(filePath, baseDir) {
75
+ const normalizedBase = path.normalize(baseDir);
76
+ const normalizedPath = path.normalize(filePath);
77
+ const relativePath = path.relative(normalizedBase, normalizedPath);
78
+
79
+ // Path is safe if it doesn't start with '..' and isn't absolute
80
+ return !relativePath.startsWith('..') && !path.isAbsolute(relativePath);
81
+ }
82
+
83
+ /**
84
+ * Extracts a tar.gz archive using the tar library
85
+ * @param {string} archivePath - Path to the .tar.gz file
86
+ * @param {string} extractDir - Directory to extract to
87
+ */
88
+ async function extractTarGz(archivePath, extractDir) {
89
+ await tar.extract({
90
+ file: archivePath,
91
+ cwd: extractDir,
92
+ // Security: Prevent path traversal attacks
93
+ onentry: (entry) => {
94
+ const fullPath = path.join(extractDir, entry.path);
95
+ if (!isPathSafe(fullPath, extractDir)) {
96
+ throw new Error(`Path traversal attempt detected: ${entry.path}`);
97
+ }
98
+ }
99
+ });
100
+ }
101
+
102
+ /**
103
+ * Extracts a zip archive using adm-zip library
104
+ * @param {string} archivePath - Path to the .zip file
105
+ * @param {string} extractDir - Directory to extract to
106
+ */
107
+ async function extractZip(archivePath, extractDir) {
108
+ const zip = new AdmZip(archivePath);
109
+ const zipEntries = zip.getEntries();
110
+
111
+ // Extract each entry with path validation
112
+ for (const entry of zipEntries) {
113
+ const outputPath = path.join(extractDir, entry.entryName);
114
+
115
+ // Security: Validate path to prevent traversal attacks
116
+ if (!isPathSafe(outputPath, extractDir)) {
117
+ throw new Error(`Path traversal attempt detected: ${entry.entryName}`);
118
+ }
119
+
120
+ if (entry.isDirectory) {
121
+ await fs.ensureDir(outputPath);
122
+ } else {
123
+ await fs.ensureDir(path.dirname(outputPath));
124
+ await fs.writeFile(outputPath, entry.getData());
125
+ }
126
+ }
127
+ }
128
+
129
+ /**
130
+ * Finds the binary file in the extracted directory
131
+ * @param {string} dir - Directory to search
132
+ * @param {string} baseDir - Base directory for path validation
133
+ * @param {string} binaryName - Name of binary to find
134
+ * @param {boolean} isWindows - Whether running on Windows
135
+ * @returns {Promise<string|null>} Path to binary or null
136
+ */
137
+ async function findBinary(dir, baseDir, binaryName, isWindows) {
138
+ const entries = await fs.readdir(dir, { withFileTypes: true });
139
+
140
+ for (const entry of entries) {
141
+ const fullPath = path.join(dir, entry.name);
142
+
143
+ // Security: Validate path to prevent traversal
144
+ if (!isPathSafe(fullPath, baseDir)) {
145
+ if (process.env.DEBUG === '1' || process.env.VERBOSE === '1') {
146
+ console.log(`Skipping unsafe path: ${fullPath}`);
147
+ }
148
+ continue;
149
+ }
150
+
151
+ if (entry.isDirectory()) {
152
+ const result = await findBinary(fullPath, baseDir, binaryName, isWindows);
153
+ if (result) return result;
154
+ } else if (entry.isFile()) {
155
+ // Check if this is the binary we're looking for
156
+ if (entry.name === binaryName ||
157
+ entry.name === BINARY_NAME ||
158
+ (isWindows && entry.name.endsWith('.exe'))) {
159
+ return fullPath;
160
+ }
161
+ }
162
+ }
163
+
164
+ return null;
165
+ }
166
+
167
+ /**
168
+ * Extracts the bundled binary for the current platform
169
+ * @param {string} version - Version string (used for archive naming)
170
+ * @returns {Promise<string>} Path to the extracted binary
171
+ */
172
+ export async function extractBundledBinary(version) {
173
+ const { platform, extension, osType } = detectPlatform();
174
+
175
+ // Construct the archive filename
176
+ const archiveName = `probe-v${version}-${platform}.${extension}`;
177
+
178
+ // Path to the bundled archive
179
+ const binariesDir = path.resolve(__dirname, '..', 'bin', 'binaries');
180
+ const archivePath = path.join(binariesDir, archiveName);
181
+
182
+ if (process.env.DEBUG === '1' || process.env.VERBOSE === '1') {
183
+ console.log(`Looking for bundled binary at: ${archivePath}`);
184
+ }
185
+
186
+ // Check if the archive exists
187
+ if (!(await fs.pathExists(archivePath))) {
188
+ throw new Error(
189
+ `Bundled binary not found for platform ${platform}.\n` +
190
+ `Expected archive: ${archiveName}\n` +
191
+ `Searched in: ${binariesDir}\n` +
192
+ `\n` +
193
+ `Supported platforms:\n` +
194
+ ` - x86_64-unknown-linux-gnu (Linux x64)\n` +
195
+ ` - aarch64-unknown-linux-gnu (Linux ARM64)\n` +
196
+ ` - x86_64-apple-darwin (macOS Intel)\n` +
197
+ ` - aarch64-apple-darwin (macOS Apple Silicon)\n` +
198
+ ` - x86_64-pc-windows-msvc (Windows x64)\n` +
199
+ `\n` +
200
+ `Your platform: ${platform}`
201
+ );
202
+ }
203
+
204
+ // Determine output binary name and path
205
+ const binDir = path.resolve(__dirname, '..', 'bin');
206
+ const isWindows = osType === 'win32';
207
+ const binaryName = isWindows ? `${BINARY_NAME}.exe` : `${BINARY_NAME}-binary`;
208
+ const binaryPath = path.join(binDir, binaryName);
209
+
210
+ if (process.env.DEBUG === '1' || process.env.VERBOSE === '1') {
211
+ console.log(`Extracting ${archiveName} to ${binDir}...`);
212
+ }
213
+
214
+ // Create a temporary extraction directory
215
+ const extractDir = path.join(binDir, 'temp_extract');
216
+ await fs.ensureDir(extractDir);
217
+
218
+ try {
219
+ // Extract based on file type using proper libraries (no shell commands!)
220
+ if (extension === 'tar.gz') {
221
+ await extractTarGz(archivePath, extractDir);
222
+ } else if (extension === 'zip') {
223
+ await extractZip(archivePath, extractDir);
224
+ }
225
+
226
+ // Find the binary in the extracted files
227
+ if (process.env.DEBUG === '1' || process.env.VERBOSE === '1') {
228
+ console.log(`Searching for binary in extracted files...`);
229
+ }
230
+
231
+ const binaryFilePath = await findBinary(extractDir, extractDir, binaryName, isWindows);
232
+
233
+ if (!binaryFilePath) {
234
+ const allFiles = await fs.readdir(extractDir, { recursive: true });
235
+ throw new Error(
236
+ `Binary not found in the archive.\n` +
237
+ `Expected binary name: ${binaryName}\n` +
238
+ `Files in archive: ${allFiles.join(', ')}`
239
+ );
240
+ }
241
+
242
+ // Copy the binary to the final location
243
+ if (process.env.DEBUG === '1' || process.env.VERBOSE === '1') {
244
+ console.log(`Found binary at ${binaryFilePath}`);
245
+ console.log(`Installing to ${binaryPath}`);
246
+ }
247
+
248
+ await fs.copyFile(binaryFilePath, binaryPath);
249
+
250
+ // Make the binary executable on Unix-like systems
251
+ if (!isWindows) {
252
+ await fs.chmod(binaryPath, 0o755);
253
+ }
254
+
255
+ // Clean up the temporary extraction directory
256
+ await fs.remove(extractDir);
257
+
258
+ if (process.env.DEBUG === '1' || process.env.VERBOSE === '1') {
259
+ console.log(`Binary successfully extracted to ${binaryPath}`);
260
+ }
261
+
262
+ return binaryPath;
263
+ } catch (error) {
264
+ // Clean up on error
265
+ try {
266
+ await fs.remove(extractDir);
267
+ } catch {}
268
+
269
+ throw new Error(`Failed to extract bundled binary: ${error.message}`);
270
+ }
271
+ }
package/build/index.js CHANGED
@@ -39,8 +39,6 @@ import { searchTool, queryTool, extractTool, delegateTool } from './tools/vercel
39
39
  import { bashTool } from './tools/bash.js';
40
40
  import { ProbeAgent } from './agent/ProbeAgent.js';
41
41
  import { SimpleTelemetry, SimpleAppTracer, initializeSimpleTelemetryFromOptions } from './agent/simpleTelemetry.js';
42
- import { TelemetryConfig, initializeTelemetryFromOptions } from './agent/telemetry.js';
43
- import { AppTracer } from './agent/appTracer.js';
44
42
  import { listFilesToolInstance, searchFilesToolInstance } from './agent/probeTool.js';
45
43
  import { StorageAdapter, InMemoryStorageAdapter } from './agent/storage/index.js';
46
44
  import { HookManager, HOOK_TYPES } from './agent/hooks/index.js';
@@ -64,14 +62,10 @@ export {
64
62
  // Export hooks
65
63
  HookManager,
66
64
  HOOK_TYPES,
67
- // Export simple telemetry classes (no OpenTelemetry dependencies)
65
+ // Export simple telemetry classes (lightweight, no heavy dependencies)
68
66
  SimpleTelemetry,
69
67
  SimpleAppTracer,
70
68
  initializeSimpleTelemetryFromOptions,
71
- // Export full OpenTelemetry telemetry classes
72
- TelemetryConfig,
73
- AppTracer,
74
- initializeTelemetryFromOptions,
75
69
  // Export tool generators directly
76
70
  searchTool,
77
71
  queryTool,