@probelabs/probe 0.6.0-rc173 → 0.6.0-rc174
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/index.js +124 -75
- package/build/agent/probeTool.js +4 -13
- package/build/downloader.js +6 -2
- package/build/extractor.js +6 -2
- package/build/utils/file-lister.js +9 -2
- package/build/utils/symlink-utils.js +63 -0
- package/cjs/agent/ProbeAgent.cjs +121 -71
- package/cjs/index.cjs +124 -74
- package/package.json +2 -2
- package/src/agent/probeTool.js +4 -13
- package/src/downloader.js +6 -2
- package/src/extractor.js +6 -2
- package/src/utils/file-lister.js +9 -2
- package/src/utils/symlink-utils.js +63 -0
package/build/agent/index.js
CHANGED
|
@@ -2210,9 +2210,50 @@ var init_directory_resolver = __esm({
|
|
|
2210
2210
|
}
|
|
2211
2211
|
});
|
|
2212
2212
|
|
|
2213
|
+
// src/utils/symlink-utils.js
|
|
2214
|
+
import fs2 from "fs";
|
|
2215
|
+
import { promises as fsPromises } from "fs";
|
|
2216
|
+
async function getEntryType(entry, fullPath) {
|
|
2217
|
+
try {
|
|
2218
|
+
const stats = await fsPromises.stat(fullPath);
|
|
2219
|
+
return {
|
|
2220
|
+
isFile: stats.isFile(),
|
|
2221
|
+
isDirectory: stats.isDirectory(),
|
|
2222
|
+
size: stats.size
|
|
2223
|
+
};
|
|
2224
|
+
} catch {
|
|
2225
|
+
return {
|
|
2226
|
+
isFile: entry.isFile(),
|
|
2227
|
+
isDirectory: entry.isDirectory(),
|
|
2228
|
+
size: 0
|
|
2229
|
+
};
|
|
2230
|
+
}
|
|
2231
|
+
}
|
|
2232
|
+
function getEntryTypeSync(entry, fullPath) {
|
|
2233
|
+
try {
|
|
2234
|
+
const stats = fs2.statSync(fullPath);
|
|
2235
|
+
return {
|
|
2236
|
+
isFile: stats.isFile(),
|
|
2237
|
+
isDirectory: stats.isDirectory(),
|
|
2238
|
+
size: stats.size
|
|
2239
|
+
};
|
|
2240
|
+
} catch {
|
|
2241
|
+
return {
|
|
2242
|
+
isFile: entry.isFile(),
|
|
2243
|
+
isDirectory: entry.isDirectory(),
|
|
2244
|
+
size: 0
|
|
2245
|
+
};
|
|
2246
|
+
}
|
|
2247
|
+
}
|
|
2248
|
+
var init_symlink_utils = __esm({
|
|
2249
|
+
"src/utils/symlink-utils.js"() {
|
|
2250
|
+
"use strict";
|
|
2251
|
+
}
|
|
2252
|
+
});
|
|
2253
|
+
|
|
2213
2254
|
// src/downloader.js
|
|
2214
2255
|
import axios from "axios";
|
|
2215
|
-
import
|
|
2256
|
+
import fs3 from "fs-extra";
|
|
2216
2257
|
import path2 from "path";
|
|
2217
2258
|
import { createHash } from "crypto";
|
|
2218
2259
|
import { promisify } from "util";
|
|
@@ -2249,7 +2290,7 @@ async function acquireFileLock(lockPath, version) {
|
|
|
2249
2290
|
timestamp: Date.now()
|
|
2250
2291
|
};
|
|
2251
2292
|
try {
|
|
2252
|
-
await
|
|
2293
|
+
await fs3.writeFile(lockPath, JSON.stringify(lockData), { flag: "wx" });
|
|
2253
2294
|
if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
|
|
2254
2295
|
console.log(`Acquired file lock: ${lockPath}`);
|
|
2255
2296
|
}
|
|
@@ -2257,13 +2298,13 @@ async function acquireFileLock(lockPath, version) {
|
|
|
2257
2298
|
} catch (error) {
|
|
2258
2299
|
if (error.code === "EEXIST") {
|
|
2259
2300
|
try {
|
|
2260
|
-
const existingLock = JSON.parse(await
|
|
2301
|
+
const existingLock = JSON.parse(await fs3.readFile(lockPath, "utf-8"));
|
|
2261
2302
|
const lockAge = Date.now() - existingLock.timestamp;
|
|
2262
2303
|
if (lockAge > LOCK_TIMEOUT_MS) {
|
|
2263
2304
|
if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
|
|
2264
2305
|
console.log(`Removing stale lock file (age: ${Math.round(lockAge / 1e3)}s, pid: ${existingLock.pid})`);
|
|
2265
2306
|
}
|
|
2266
|
-
await
|
|
2307
|
+
await fs3.remove(lockPath);
|
|
2267
2308
|
return false;
|
|
2268
2309
|
}
|
|
2269
2310
|
if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
|
|
@@ -2275,7 +2316,7 @@ async function acquireFileLock(lockPath, version) {
|
|
|
2275
2316
|
console.log(`Lock file corrupted, removing: ${readError.message}`);
|
|
2276
2317
|
}
|
|
2277
2318
|
try {
|
|
2278
|
-
await
|
|
2319
|
+
await fs3.remove(lockPath);
|
|
2279
2320
|
} catch {
|
|
2280
2321
|
}
|
|
2281
2322
|
return false;
|
|
@@ -2297,7 +2338,7 @@ async function acquireFileLock(lockPath, version) {
|
|
|
2297
2338
|
}
|
|
2298
2339
|
async function releaseFileLock(lockPath) {
|
|
2299
2340
|
try {
|
|
2300
|
-
await
|
|
2341
|
+
await fs3.remove(lockPath);
|
|
2301
2342
|
if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
|
|
2302
2343
|
console.log(`Released file lock: ${lockPath}`);
|
|
2303
2344
|
}
|
|
@@ -2310,13 +2351,13 @@ async function releaseFileLock(lockPath) {
|
|
|
2310
2351
|
async function waitForFileLock(lockPath, binaryPath) {
|
|
2311
2352
|
const startTime = Date.now();
|
|
2312
2353
|
while (Date.now() - startTime < MAX_LOCK_WAIT_MS) {
|
|
2313
|
-
if (await
|
|
2354
|
+
if (await fs3.pathExists(binaryPath)) {
|
|
2314
2355
|
if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
|
|
2315
2356
|
console.log(`Binary now available at ${binaryPath}, download completed by another process`);
|
|
2316
2357
|
}
|
|
2317
2358
|
return true;
|
|
2318
2359
|
}
|
|
2319
|
-
const lockExists = await
|
|
2360
|
+
const lockExists = await fs3.pathExists(lockPath);
|
|
2320
2361
|
if (!lockExists) {
|
|
2321
2362
|
if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
|
|
2322
2363
|
console.log(`Lock file removed but binary not found - download may have failed`);
|
|
@@ -2324,7 +2365,7 @@ async function waitForFileLock(lockPath, binaryPath) {
|
|
|
2324
2365
|
return false;
|
|
2325
2366
|
}
|
|
2326
2367
|
try {
|
|
2327
|
-
const lockData = JSON.parse(await
|
|
2368
|
+
const lockData = JSON.parse(await fs3.readFile(lockPath, "utf-8"));
|
|
2328
2369
|
const lockAge = Date.now() - lockData.timestamp;
|
|
2329
2370
|
if (lockAge > LOCK_TIMEOUT_MS) {
|
|
2330
2371
|
if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
|
|
@@ -2642,13 +2683,13 @@ function findBestAsset(assets, osInfo, archInfo) {
|
|
|
2642
2683
|
return bestAsset;
|
|
2643
2684
|
}
|
|
2644
2685
|
async function downloadAsset(asset, outputDir) {
|
|
2645
|
-
await
|
|
2686
|
+
await fs3.ensureDir(outputDir);
|
|
2646
2687
|
const assetPath = path2.join(outputDir, asset.name);
|
|
2647
2688
|
if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
|
|
2648
2689
|
console.log(`Downloading ${asset.name}...`);
|
|
2649
2690
|
}
|
|
2650
2691
|
const assetResponse = await axios.get(asset.url, { responseType: "arraybuffer" });
|
|
2651
|
-
await
|
|
2692
|
+
await fs3.writeFile(assetPath, Buffer.from(assetResponse.data));
|
|
2652
2693
|
const checksumUrl = asset.checksumUrl || `${asset.url}.sha256`;
|
|
2653
2694
|
const checksumFileName = asset.checksumName || `${asset.name}.sha256`;
|
|
2654
2695
|
let checksumPath = null;
|
|
@@ -2658,7 +2699,7 @@ async function downloadAsset(asset, outputDir) {
|
|
|
2658
2699
|
}
|
|
2659
2700
|
const checksumResponse = await axios.get(checksumUrl);
|
|
2660
2701
|
checksumPath = path2.join(outputDir, checksumFileName);
|
|
2661
|
-
await
|
|
2702
|
+
await fs3.writeFile(checksumPath, checksumResponse.data);
|
|
2662
2703
|
} catch (error) {
|
|
2663
2704
|
if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
|
|
2664
2705
|
console.log("No checksum file found, skipping verification");
|
|
@@ -2673,9 +2714,9 @@ async function verifyChecksum(assetPath, checksumPath) {
|
|
|
2673
2714
|
if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
|
|
2674
2715
|
console.log(`Verifying checksum...`);
|
|
2675
2716
|
}
|
|
2676
|
-
const checksumContent = await
|
|
2717
|
+
const checksumContent = await fs3.readFile(checksumPath, "utf-8");
|
|
2677
2718
|
const expectedChecksum = checksumContent.trim().split(" ")[0];
|
|
2678
|
-
const fileBuffer = await
|
|
2719
|
+
const fileBuffer = await fs3.readFile(assetPath);
|
|
2679
2720
|
const actualChecksum = createHash("sha256").update(fileBuffer).digest("hex");
|
|
2680
2721
|
if (expectedChecksum !== actualChecksum) {
|
|
2681
2722
|
console.error(`Checksum verification failed!`);
|
|
@@ -2698,7 +2739,7 @@ async function extractBinary(assetPath, outputDir) {
|
|
|
2698
2739
|
const binaryPath = path2.join(outputDir, binaryName);
|
|
2699
2740
|
try {
|
|
2700
2741
|
const extractDir = path2.join(outputDir, "temp_extract");
|
|
2701
|
-
await
|
|
2742
|
+
await fs3.ensureDir(extractDir);
|
|
2702
2743
|
if (assetName.endsWith(".tar.gz") || assetName.endsWith(".tgz")) {
|
|
2703
2744
|
if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
|
|
2704
2745
|
console.log(`Extracting tar.gz to ${extractDir}...`);
|
|
@@ -2716,11 +2757,11 @@ async function extractBinary(assetPath, outputDir) {
|
|
|
2716
2757
|
if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
|
|
2717
2758
|
console.log(`Copying binary directly to ${binaryPath}`);
|
|
2718
2759
|
}
|
|
2719
|
-
await
|
|
2760
|
+
await fs3.copyFile(assetPath, binaryPath);
|
|
2720
2761
|
if (!isWindows) {
|
|
2721
|
-
await
|
|
2762
|
+
await fs3.chmod(binaryPath, 493);
|
|
2722
2763
|
}
|
|
2723
|
-
await
|
|
2764
|
+
await fs3.remove(extractDir);
|
|
2724
2765
|
if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
|
|
2725
2766
|
console.log(`Binary installed to ${binaryPath}`);
|
|
2726
2767
|
}
|
|
@@ -2730,13 +2771,14 @@ async function extractBinary(assetPath, outputDir) {
|
|
|
2730
2771
|
console.log(`Searching for binary in extracted files...`);
|
|
2731
2772
|
}
|
|
2732
2773
|
const findBinary = async (dir) => {
|
|
2733
|
-
const entries = await
|
|
2774
|
+
const entries = await fs3.readdir(dir, { withFileTypes: true });
|
|
2734
2775
|
for (const entry of entries) {
|
|
2735
2776
|
const fullPath = path2.join(dir, entry.name);
|
|
2736
|
-
|
|
2777
|
+
const entryType = await getEntryType(entry, fullPath);
|
|
2778
|
+
if (entryType.isDirectory) {
|
|
2737
2779
|
const result = await findBinary(fullPath);
|
|
2738
2780
|
if (result) return result;
|
|
2739
|
-
} else if (
|
|
2781
|
+
} else if (entryType.isFile) {
|
|
2740
2782
|
if (entry.name === binaryName || entry.name === BINARY_NAME || isWindows && entry.name.endsWith(".exe")) {
|
|
2741
2783
|
return fullPath;
|
|
2742
2784
|
}
|
|
@@ -2746,7 +2788,7 @@ async function extractBinary(assetPath, outputDir) {
|
|
|
2746
2788
|
};
|
|
2747
2789
|
const binaryFilePath = await findBinary(extractDir);
|
|
2748
2790
|
if (!binaryFilePath) {
|
|
2749
|
-
const allFiles = await
|
|
2791
|
+
const allFiles = await fs3.readdir(extractDir, { recursive: true });
|
|
2750
2792
|
console.error(`Binary not found in extracted files. Found: ${allFiles.join(", ")}`);
|
|
2751
2793
|
throw new Error(`Binary not found in the archive.`);
|
|
2752
2794
|
}
|
|
@@ -2754,11 +2796,11 @@ async function extractBinary(assetPath, outputDir) {
|
|
|
2754
2796
|
console.log(`Found binary at ${binaryFilePath}`);
|
|
2755
2797
|
console.log(`Copying binary to ${binaryPath}`);
|
|
2756
2798
|
}
|
|
2757
|
-
await
|
|
2799
|
+
await fs3.copyFile(binaryFilePath, binaryPath);
|
|
2758
2800
|
if (!isWindows) {
|
|
2759
|
-
await
|
|
2801
|
+
await fs3.chmod(binaryPath, 493);
|
|
2760
2802
|
}
|
|
2761
|
-
await
|
|
2803
|
+
await fs3.remove(extractDir);
|
|
2762
2804
|
if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
|
|
2763
2805
|
console.log(`Binary successfully installed to ${binaryPath}`);
|
|
2764
2806
|
}
|
|
@@ -2771,8 +2813,8 @@ async function extractBinary(assetPath, outputDir) {
|
|
|
2771
2813
|
async function getVersionInfo(binDir) {
|
|
2772
2814
|
try {
|
|
2773
2815
|
const versionInfoPath = path2.join(binDir, "version-info.json");
|
|
2774
|
-
if (await
|
|
2775
|
-
const content = await
|
|
2816
|
+
if (await fs3.pathExists(versionInfoPath)) {
|
|
2817
|
+
const content = await fs3.readFile(versionInfoPath, "utf-8");
|
|
2776
2818
|
return JSON.parse(content);
|
|
2777
2819
|
}
|
|
2778
2820
|
return null;
|
|
@@ -2787,7 +2829,7 @@ async function saveVersionInfo(version, binDir) {
|
|
|
2787
2829
|
lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
|
|
2788
2830
|
};
|
|
2789
2831
|
const versionInfoPath = path2.join(binDir, "version-info.json");
|
|
2790
|
-
await
|
|
2832
|
+
await fs3.writeFile(versionInfoPath, JSON.stringify(versionInfo, null, 2));
|
|
2791
2833
|
if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
|
|
2792
2834
|
console.log(`Version info saved: ${version} at ${versionInfoPath}`);
|
|
2793
2835
|
}
|
|
@@ -2802,11 +2844,11 @@ async function getPackageVersion() {
|
|
|
2802
2844
|
];
|
|
2803
2845
|
for (const packageJsonPath of possiblePaths) {
|
|
2804
2846
|
try {
|
|
2805
|
-
if (
|
|
2847
|
+
if (fs3.existsSync(packageJsonPath)) {
|
|
2806
2848
|
if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
|
|
2807
2849
|
console.log(`Found package.json at: ${packageJsonPath}`);
|
|
2808
2850
|
}
|
|
2809
|
-
const packageJson = JSON.parse(
|
|
2851
|
+
const packageJson = JSON.parse(fs3.readFileSync(packageJsonPath, "utf-8"));
|
|
2810
2852
|
if (packageJson.version) {
|
|
2811
2853
|
if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
|
|
2812
2854
|
console.log(`Using version from package.json: ${packageJson.version}`);
|
|
@@ -2862,9 +2904,9 @@ async function doDownload(version) {
|
|
|
2862
2904
|
const extractedBinaryPath = await extractBinary(assetPath, localDir);
|
|
2863
2905
|
await saveVersionInfo(tagVersion, localDir);
|
|
2864
2906
|
try {
|
|
2865
|
-
await
|
|
2907
|
+
await fs3.remove(assetPath);
|
|
2866
2908
|
if (checksumPath) {
|
|
2867
|
-
await
|
|
2909
|
+
await fs3.remove(checksumPath);
|
|
2868
2910
|
}
|
|
2869
2911
|
} catch (err) {
|
|
2870
2912
|
if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
|
|
@@ -2885,7 +2927,7 @@ async function downloadProbeBinary(version) {
|
|
|
2885
2927
|
const isWindows = os2.platform() === "win32";
|
|
2886
2928
|
const binaryName = isWindows ? `${BINARY_NAME}.exe` : `${BINARY_NAME}-binary`;
|
|
2887
2929
|
const binaryPath = path2.join(localDir, binaryName);
|
|
2888
|
-
if (await
|
|
2930
|
+
if (await fs3.pathExists(binaryPath)) {
|
|
2889
2931
|
const versionInfo = await getVersionInfo(localDir);
|
|
2890
2932
|
if (versionInfo && versionInfo.version === version) {
|
|
2891
2933
|
if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
|
|
@@ -2940,6 +2982,7 @@ var init_downloader = __esm({
|
|
|
2940
2982
|
"use strict";
|
|
2941
2983
|
init_utils();
|
|
2942
2984
|
init_directory_resolver();
|
|
2985
|
+
init_symlink_utils();
|
|
2943
2986
|
exec = promisify(execCallback);
|
|
2944
2987
|
REPO_OWNER = "probelabs";
|
|
2945
2988
|
REPO_NAME = "probe";
|
|
@@ -2955,11 +2998,11 @@ var init_downloader = __esm({
|
|
|
2955
2998
|
|
|
2956
2999
|
// src/utils.js
|
|
2957
3000
|
import path3 from "path";
|
|
2958
|
-
import
|
|
3001
|
+
import fs4 from "fs-extra";
|
|
2959
3002
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
2960
3003
|
async function getBinaryPath(options = {}) {
|
|
2961
3004
|
const { forceDownload = false, version } = options;
|
|
2962
|
-
if (process.env.PROBE_PATH &&
|
|
3005
|
+
if (process.env.PROBE_PATH && fs4.existsSync(process.env.PROBE_PATH) && !forceDownload) {
|
|
2963
3006
|
probeBinaryPath = process.env.PROBE_PATH;
|
|
2964
3007
|
return probeBinaryPath;
|
|
2965
3008
|
}
|
|
@@ -2972,13 +3015,13 @@ async function getBinaryPath(options = {}) {
|
|
|
2972
3015
|
const binaryName = isWindows ? "probe.exe" : "probe-binary";
|
|
2973
3016
|
const localPackageBin = path3.resolve(__dirname3, "..", "bin");
|
|
2974
3017
|
const localBinaryPath = path3.join(localPackageBin, binaryName);
|
|
2975
|
-
if (
|
|
3018
|
+
if (fs4.existsSync(localBinaryPath) && !forceDownload) {
|
|
2976
3019
|
probeBinaryPath = localBinaryPath;
|
|
2977
3020
|
return probeBinaryPath;
|
|
2978
3021
|
}
|
|
2979
3022
|
const binDir = await getPackageBinDir();
|
|
2980
3023
|
const binaryPath = path3.join(binDir, binaryName);
|
|
2981
|
-
if (
|
|
3024
|
+
if (fs4.existsSync(binaryPath) && !forceDownload) {
|
|
2982
3025
|
probeBinaryPath = binaryPath;
|
|
2983
3026
|
return probeBinaryPath;
|
|
2984
3027
|
}
|
|
@@ -9674,7 +9717,7 @@ Command failed with exit code ${result.exitCode}`;
|
|
|
9674
9717
|
|
|
9675
9718
|
// src/tools/edit.js
|
|
9676
9719
|
import { tool as tool3 } from "ai";
|
|
9677
|
-
import { promises as
|
|
9720
|
+
import { promises as fs5 } from "fs";
|
|
9678
9721
|
import { dirname, resolve as resolve3, isAbsolute, sep } from "path";
|
|
9679
9722
|
import { existsSync as existsSync2 } from "fs";
|
|
9680
9723
|
function isPathAllowed(filePath, allowedFolders) {
|
|
@@ -9762,7 +9805,7 @@ Important:
|
|
|
9762
9805
|
if (!existsSync2(resolvedPath)) {
|
|
9763
9806
|
return `Error editing file: File not found - ${file_path}`;
|
|
9764
9807
|
}
|
|
9765
|
-
const content = await
|
|
9808
|
+
const content = await fs5.readFile(resolvedPath, "utf-8");
|
|
9766
9809
|
if (!content.includes(old_string)) {
|
|
9767
9810
|
return `Error editing file: String not found - the specified old_string was not found in ${file_path}`;
|
|
9768
9811
|
}
|
|
@@ -9779,7 +9822,7 @@ Important:
|
|
|
9779
9822
|
if (newContent === content) {
|
|
9780
9823
|
return `Error editing file: No changes made - old_string and new_string might be the same`;
|
|
9781
9824
|
}
|
|
9782
|
-
await
|
|
9825
|
+
await fs5.writeFile(resolvedPath, newContent, "utf-8");
|
|
9783
9826
|
const replacedCount = replace_all ? occurrences : 1;
|
|
9784
9827
|
if (debug) {
|
|
9785
9828
|
console.error(`[Edit] Successfully edited ${resolvedPath}, replaced ${replacedCount} occurrence(s)`);
|
|
@@ -9847,8 +9890,8 @@ Important:
|
|
|
9847
9890
|
return `Error creating file: File already exists - ${file_path}. Use overwrite: true to replace it.`;
|
|
9848
9891
|
}
|
|
9849
9892
|
const dir = dirname(resolvedPath);
|
|
9850
|
-
await
|
|
9851
|
-
await
|
|
9893
|
+
await fs5.mkdir(dir, { recursive: true });
|
|
9894
|
+
await fs5.writeFile(resolvedPath, content, "utf-8");
|
|
9852
9895
|
const action = existsSync2(resolvedPath) && overwrite ? "overwrote" : "created";
|
|
9853
9896
|
const bytes = Buffer.byteLength(content, "utf-8");
|
|
9854
9897
|
if (debug) {
|
|
@@ -10118,7 +10161,7 @@ var init_tools = __esm({
|
|
|
10118
10161
|
});
|
|
10119
10162
|
|
|
10120
10163
|
// src/utils/file-lister.js
|
|
10121
|
-
import
|
|
10164
|
+
import fs6 from "fs";
|
|
10122
10165
|
import path4 from "path";
|
|
10123
10166
|
import { promisify as promisify6 } from "util";
|
|
10124
10167
|
import { exec as exec4 } from "child_process";
|
|
@@ -10128,10 +10171,10 @@ async function listFilesByLevel(options) {
|
|
|
10128
10171
|
maxFiles = 100,
|
|
10129
10172
|
respectGitignore = true
|
|
10130
10173
|
} = options;
|
|
10131
|
-
if (!
|
|
10174
|
+
if (!fs6.existsSync(directory)) {
|
|
10132
10175
|
throw new Error(`Directory does not exist: ${directory}`);
|
|
10133
10176
|
}
|
|
10134
|
-
const gitDirExists =
|
|
10177
|
+
const gitDirExists = fs6.existsSync(path4.join(directory, ".git"));
|
|
10135
10178
|
if (gitDirExists && respectGitignore) {
|
|
10136
10179
|
try {
|
|
10137
10180
|
return await listFilesUsingGit(directory, maxFiles);
|
|
@@ -10162,8 +10205,11 @@ async function listFilesByLevelManually(directory, maxFiles, respectGitignore) {
|
|
|
10162
10205
|
while (queue.length > 0 && result.length < maxFiles) {
|
|
10163
10206
|
const { dir, level } = queue.shift();
|
|
10164
10207
|
try {
|
|
10165
|
-
const entries =
|
|
10166
|
-
const files = entries.filter((entry) =>
|
|
10208
|
+
const entries = fs6.readdirSync(dir, { withFileTypes: true });
|
|
10209
|
+
const files = entries.filter((entry) => {
|
|
10210
|
+
const fullPath = path4.join(dir, entry.name);
|
|
10211
|
+
return getEntryTypeSync(entry, fullPath).isFile;
|
|
10212
|
+
});
|
|
10167
10213
|
for (const file of files) {
|
|
10168
10214
|
if (result.length >= maxFiles) break;
|
|
10169
10215
|
const filePath = path4.join(dir, file.name);
|
|
@@ -10171,7 +10217,10 @@ async function listFilesByLevelManually(directory, maxFiles, respectGitignore) {
|
|
|
10171
10217
|
if (shouldIgnore(relativePath, ignorePatterns)) continue;
|
|
10172
10218
|
result.push(relativePath);
|
|
10173
10219
|
}
|
|
10174
|
-
const dirs = entries.filter((entry) =>
|
|
10220
|
+
const dirs = entries.filter((entry) => {
|
|
10221
|
+
const fullPath = path4.join(dir, entry.name);
|
|
10222
|
+
return getEntryTypeSync(entry, fullPath).isDirectory;
|
|
10223
|
+
});
|
|
10175
10224
|
for (const subdir of dirs) {
|
|
10176
10225
|
const subdirPath = path4.join(dir, subdir.name);
|
|
10177
10226
|
const relativeSubdirPath = path4.relative(directory, subdirPath);
|
|
@@ -10187,11 +10236,11 @@ async function listFilesByLevelManually(directory, maxFiles, respectGitignore) {
|
|
|
10187
10236
|
}
|
|
10188
10237
|
function loadGitignorePatterns(directory) {
|
|
10189
10238
|
const gitignorePath = path4.join(directory, ".gitignore");
|
|
10190
|
-
if (!
|
|
10239
|
+
if (!fs6.existsSync(gitignorePath)) {
|
|
10191
10240
|
return [];
|
|
10192
10241
|
}
|
|
10193
10242
|
try {
|
|
10194
|
-
const content =
|
|
10243
|
+
const content = fs6.readFileSync(gitignorePath, "utf8");
|
|
10195
10244
|
return content.split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#"));
|
|
10196
10245
|
} catch (error) {
|
|
10197
10246
|
console.error(`Warning: Could not read .gitignore: ${error.message}`);
|
|
@@ -10213,6 +10262,7 @@ var execAsync3;
|
|
|
10213
10262
|
var init_file_lister = __esm({
|
|
10214
10263
|
"src/utils/file-lister.js"() {
|
|
10215
10264
|
"use strict";
|
|
10265
|
+
init_symlink_utils();
|
|
10216
10266
|
execAsync3 = promisify6(exec4);
|
|
10217
10267
|
}
|
|
10218
10268
|
});
|
|
@@ -15401,8 +15451,8 @@ var init_esm4 = __esm({
|
|
|
15401
15451
|
*
|
|
15402
15452
|
* @internal
|
|
15403
15453
|
*/
|
|
15404
|
-
constructor(cwd = process.cwd(), pathImpl, sep4, { nocase, childrenCacheSize = 16 * 1024, fs:
|
|
15405
|
-
this.#fs = fsFromOption(
|
|
15454
|
+
constructor(cwd = process.cwd(), pathImpl, sep4, { nocase, childrenCacheSize = 16 * 1024, fs: fs9 = defaultFS } = {}) {
|
|
15455
|
+
this.#fs = fsFromOption(fs9);
|
|
15406
15456
|
if (cwd instanceof URL || cwd.startsWith("file://")) {
|
|
15407
15457
|
cwd = fileURLToPath4(cwd);
|
|
15408
15458
|
}
|
|
@@ -15960,8 +16010,8 @@ var init_esm4 = __esm({
|
|
|
15960
16010
|
/**
|
|
15961
16011
|
* @internal
|
|
15962
16012
|
*/
|
|
15963
|
-
newRoot(
|
|
15964
|
-
return new PathWin32(this.rootPath, IFDIR, void 0, this.roots, this.nocase, this.childrenCache(), { fs:
|
|
16013
|
+
newRoot(fs9) {
|
|
16014
|
+
return new PathWin32(this.rootPath, IFDIR, void 0, this.roots, this.nocase, this.childrenCache(), { fs: fs9 });
|
|
15965
16015
|
}
|
|
15966
16016
|
/**
|
|
15967
16017
|
* Return true if the provided path string is an absolute path
|
|
@@ -15989,8 +16039,8 @@ var init_esm4 = __esm({
|
|
|
15989
16039
|
/**
|
|
15990
16040
|
* @internal
|
|
15991
16041
|
*/
|
|
15992
|
-
newRoot(
|
|
15993
|
-
return new PathPosix(this.rootPath, IFDIR, void 0, this.roots, this.nocase, this.childrenCache(), { fs:
|
|
16042
|
+
newRoot(fs9) {
|
|
16043
|
+
return new PathPosix(this.rootPath, IFDIR, void 0, this.roots, this.nocase, this.childrenCache(), { fs: fs9 });
|
|
15994
16044
|
}
|
|
15995
16045
|
/**
|
|
15996
16046
|
* Return true if the provided path string is an absolute path
|
|
@@ -17130,8 +17180,8 @@ import { exec as exec5 } from "child_process";
|
|
|
17130
17180
|
import { promisify as promisify7 } from "util";
|
|
17131
17181
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
17132
17182
|
import { EventEmitter as EventEmitter2 } from "events";
|
|
17133
|
-
import
|
|
17134
|
-
import { promises as
|
|
17183
|
+
import fs7 from "fs";
|
|
17184
|
+
import { promises as fsPromises2 } from "fs";
|
|
17135
17185
|
import path6 from "path";
|
|
17136
17186
|
function isSessionCancelled(sessionId) {
|
|
17137
17187
|
return activeToolExecutions.get(sessionId)?.cancelled || false;
|
|
@@ -17212,6 +17262,7 @@ var init_probeTool = __esm({
|
|
|
17212
17262
|
"use strict";
|
|
17213
17263
|
init_index();
|
|
17214
17264
|
init_esm5();
|
|
17265
|
+
init_symlink_utils();
|
|
17215
17266
|
toolCallEmitter = new EventEmitter2();
|
|
17216
17267
|
activeToolExecutions = /* @__PURE__ */ new Map();
|
|
17217
17268
|
wrapToolWithEmitter = (tool4, toolName, baseExecute) => {
|
|
@@ -17317,7 +17368,7 @@ var init_probeTool = __esm({
|
|
|
17317
17368
|
console.log(`[DEBUG] Listing files in directory: ${targetDir}`);
|
|
17318
17369
|
}
|
|
17319
17370
|
try {
|
|
17320
|
-
const files = await
|
|
17371
|
+
const files = await fsPromises2.readdir(targetDir, { withFileTypes: true });
|
|
17321
17372
|
const formatSize = (size) => {
|
|
17322
17373
|
if (size < 1024) return `${size}B`;
|
|
17323
17374
|
if (size < 1024 * 1024) return `${(size / 1024).toFixed(1)}K`;
|
|
@@ -17325,21 +17376,12 @@ var init_probeTool = __esm({
|
|
|
17325
17376
|
return `${(size / (1024 * 1024 * 1024)).toFixed(1)}G`;
|
|
17326
17377
|
};
|
|
17327
17378
|
const entries = await Promise.all(files.map(async (file) => {
|
|
17328
|
-
const isDirectory = file.isDirectory();
|
|
17329
17379
|
const fullPath = path6.join(targetDir, file.name);
|
|
17330
|
-
|
|
17331
|
-
try {
|
|
17332
|
-
const stats = await fsPromises.stat(fullPath);
|
|
17333
|
-
size = stats.size;
|
|
17334
|
-
} catch (statError) {
|
|
17335
|
-
if (debug) {
|
|
17336
|
-
console.log(`[DEBUG] Could not stat file ${file.name}:`, statError.message);
|
|
17337
|
-
}
|
|
17338
|
-
}
|
|
17380
|
+
const entryType = await getEntryType(file, fullPath);
|
|
17339
17381
|
return {
|
|
17340
17382
|
name: file.name,
|
|
17341
|
-
isDirectory,
|
|
17342
|
-
size
|
|
17383
|
+
isDirectory: entryType.isDirectory,
|
|
17384
|
+
size: entryType.size
|
|
17343
17385
|
};
|
|
17344
17386
|
}));
|
|
17345
17387
|
entries.sort((a, b) => {
|
|
@@ -31858,7 +31900,7 @@ function validateFlowchart(text, options = {}) {
|
|
|
31858
31900
|
const byLine = /* @__PURE__ */ new Map();
|
|
31859
31901
|
const collect = (arr) => {
|
|
31860
31902
|
for (const e of arr || []) {
|
|
31861
|
-
if (e && (e.code === "FL-LABEL-PARENS-UNQUOTED" || e.code === "FL-LABEL-AT-IN-UNQUOTED")) {
|
|
31903
|
+
if (e && (e.code === "FL-LABEL-PARENS-UNQUOTED" || e.code === "FL-LABEL-AT-IN-UNQUOTED" || e.code === "FL-LABEL-QUOTE-IN-UNQUOTED")) {
|
|
31862
31904
|
const ln = e.line ?? 0;
|
|
31863
31905
|
const col = e.column ?? 1;
|
|
31864
31906
|
const list = byLine.get(ln) || [];
|
|
@@ -31939,6 +31981,8 @@ function validateFlowchart(text, options = {}) {
|
|
|
31939
31981
|
const covered = existing.some((r) => !(endCol < r.start || startCol > r.end));
|
|
31940
31982
|
const hasParens = seg.includes("(") || seg.includes(")");
|
|
31941
31983
|
const hasAt = seg.includes("@");
|
|
31984
|
+
const hasQuote = seg.includes('"');
|
|
31985
|
+
const isSingleQuoted = /^'[^]*'$/.test(trimmed);
|
|
31942
31986
|
if (!covered && !isQuoted && !isParenWrapped && hasParens) {
|
|
31943
31987
|
errs.push({ line: ln, column: startCol, severity: "error", code: "FL-LABEL-PARENS-UNQUOTED", message: "Parentheses inside an unquoted label are not supported by Mermaid.", hint: 'Wrap the label in quotes, e.g., A["Mark (X)"] \u2014 or replace ( and ) with HTML entities: ( and ).' });
|
|
31944
31988
|
existing.push({ start: startCol, end: endCol });
|
|
@@ -31949,6 +31993,11 @@ function validateFlowchart(text, options = {}) {
|
|
|
31949
31993
|
existing.push({ start: startCol, end: endCol });
|
|
31950
31994
|
byLine.set(ln, existing);
|
|
31951
31995
|
}
|
|
31996
|
+
if (!covered && !isQuoted && !isSlashPair && hasQuote && !isSingleQuoted) {
|
|
31997
|
+
errs.push({ line: ln, column: startCol, severity: "error", code: "FL-LABEL-QUOTE-IN-UNQUOTED", message: "Quotes are not allowed inside unquoted node labels. Use " for quotes or wrap the entire label in quotes.", hint: 'Example: C["HTML Output: data-trigger-visibility="true""]' });
|
|
31998
|
+
existing.push({ start: startCol, end: endCol });
|
|
31999
|
+
byLine.set(ln, existing);
|
|
32000
|
+
}
|
|
31952
32001
|
i = j;
|
|
31953
32002
|
continue;
|
|
31954
32003
|
} else {
|
|
@@ -58258,7 +58307,7 @@ __export(enhanced_claude_code_exports, {
|
|
|
58258
58307
|
});
|
|
58259
58308
|
import { spawn as spawn3 } from "child_process";
|
|
58260
58309
|
import { randomBytes } from "crypto";
|
|
58261
|
-
import
|
|
58310
|
+
import fs8 from "fs/promises";
|
|
58262
58311
|
import path7 from "path";
|
|
58263
58312
|
import os3 from "os";
|
|
58264
58313
|
import { EventEmitter as EventEmitter4 } from "events";
|
|
@@ -58295,7 +58344,7 @@ async function createEnhancedClaudeCLIEngine(options = {}) {
|
|
|
58295
58344
|
}
|
|
58296
58345
|
}
|
|
58297
58346
|
};
|
|
58298
|
-
await
|
|
58347
|
+
await fs8.writeFile(mcpConfigPath, JSON.stringify(mcpConfig, null, 2));
|
|
58299
58348
|
}
|
|
58300
58349
|
if (debug) {
|
|
58301
58350
|
console.log("[DEBUG] Enhanced Claude Code Engine");
|
|
@@ -58489,7 +58538,7 @@ ${opts.schema}`;
|
|
|
58489
58538
|
}
|
|
58490
58539
|
}
|
|
58491
58540
|
if (mcpConfigPath) {
|
|
58492
|
-
await
|
|
58541
|
+
await fs8.unlink(mcpConfigPath).catch(() => {
|
|
58493
58542
|
});
|
|
58494
58543
|
if (debug) {
|
|
58495
58544
|
console.log("[DEBUG] MCP config file removed");
|
package/build/agent/probeTool.js
CHANGED
|
@@ -8,6 +8,7 @@ import fs from 'fs';
|
|
|
8
8
|
import { promises as fsPromises } from 'fs';
|
|
9
9
|
import path from 'path';
|
|
10
10
|
import { glob } from 'glob';
|
|
11
|
+
import { getEntryType } from '../utils/symlink-utils.js';
|
|
11
12
|
|
|
12
13
|
// Create an event emitter for tool calls (simplified for single-shot operations)
|
|
13
14
|
export const toolCallEmitter = new EventEmitter();
|
|
@@ -287,23 +288,13 @@ export const listFilesTool = {
|
|
|
287
288
|
|
|
288
289
|
// Format the results as ls-style output
|
|
289
290
|
const entries = await Promise.all(files.map(async (file) => {
|
|
290
|
-
const isDirectory = file.isDirectory();
|
|
291
291
|
const fullPath = path.join(targetDir, file.name);
|
|
292
|
-
|
|
293
|
-
let size = 0;
|
|
294
|
-
try {
|
|
295
|
-
const stats = await fsPromises.stat(fullPath);
|
|
296
|
-
size = stats.size;
|
|
297
|
-
} catch (statError) {
|
|
298
|
-
if (debug) {
|
|
299
|
-
console.log(`[DEBUG] Could not stat file ${file.name}:`, statError.message);
|
|
300
|
-
}
|
|
301
|
-
}
|
|
292
|
+
const entryType = await getEntryType(file, fullPath);
|
|
302
293
|
|
|
303
294
|
return {
|
|
304
295
|
name: file.name,
|
|
305
|
-
isDirectory,
|
|
306
|
-
size
|
|
296
|
+
isDirectory: entryType.isDirectory,
|
|
297
|
+
size: entryType.size
|
|
307
298
|
};
|
|
308
299
|
}));
|
|
309
300
|
|
package/build/downloader.js
CHANGED
|
@@ -14,6 +14,7 @@ import os from 'os';
|
|
|
14
14
|
import { fileURLToPath } from 'url';
|
|
15
15
|
import { ensureBinDirectory } from './utils.js';
|
|
16
16
|
import { getPackageBinDir } from './directory-resolver.js';
|
|
17
|
+
import { getEntryType } from './utils/symlink-utils.js';
|
|
17
18
|
|
|
18
19
|
const exec = promisify(execCallback);
|
|
19
20
|
|
|
@@ -770,10 +771,13 @@ async function extractBinary(assetPath, outputDir) {
|
|
|
770
771
|
for (const entry of entries) {
|
|
771
772
|
const fullPath = path.join(dir, entry.name);
|
|
772
773
|
|
|
773
|
-
|
|
774
|
+
// Use shared utility to follow symlinks and get actual target type
|
|
775
|
+
const entryType = await getEntryType(entry, fullPath);
|
|
776
|
+
|
|
777
|
+
if (entryType.isDirectory) {
|
|
774
778
|
const result = await findBinary(fullPath);
|
|
775
779
|
if (result) return result;
|
|
776
|
-
} else if (
|
|
780
|
+
} else if (entryType.isFile) {
|
|
777
781
|
// Check if this is the binary we're looking for
|
|
778
782
|
if (entry.name === binaryName ||
|
|
779
783
|
entry.name === BINARY_NAME ||
|
package/build/extractor.js
CHANGED
|
@@ -9,6 +9,7 @@ import tar from 'tar';
|
|
|
9
9
|
import AdmZip from 'adm-zip';
|
|
10
10
|
import os from 'os';
|
|
11
11
|
import { fileURLToPath } from 'url';
|
|
12
|
+
import { getEntryType } from './utils/symlink-utils.js';
|
|
12
13
|
|
|
13
14
|
const __filename = fileURLToPath(import.meta.url);
|
|
14
15
|
const __dirname = path.dirname(__filename);
|
|
@@ -148,10 +149,13 @@ async function findBinary(dir, baseDir, binaryName, isWindows) {
|
|
|
148
149
|
continue;
|
|
149
150
|
}
|
|
150
151
|
|
|
151
|
-
|
|
152
|
+
// Use shared utility to follow symlinks and get actual target type
|
|
153
|
+
const entryType = await getEntryType(entry, fullPath);
|
|
154
|
+
|
|
155
|
+
if (entryType.isDirectory) {
|
|
152
156
|
const result = await findBinary(fullPath, baseDir, binaryName, isWindows);
|
|
153
157
|
if (result) return result;
|
|
154
|
-
} else if (
|
|
158
|
+
} else if (entryType.isFile) {
|
|
155
159
|
// Check if this is the binary we're looking for
|
|
156
160
|
if (entry.name === binaryName ||
|
|
157
161
|
entry.name === BINARY_NAME ||
|
|
@@ -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);
|