@probelabs/probe 0.6.0-rc142 → 0.6.0-rc143
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 +226 -43
- package/build/downloader.js +343 -55
- package/build/mcp/index.js +9 -2
- package/build/mcp/index.ts +9 -2
- package/build/utils.js +9 -7
- package/cjs/agent/ProbeAgent.cjs +226 -43
- package/cjs/index.cjs +226 -43
- package/package.json +1 -1
- package/src/downloader.js +343 -55
- package/src/mcp/index.ts +9 -2
- package/src/utils.js +9 -7
package/build/agent/index.js
CHANGED
|
@@ -1876,6 +1876,145 @@ import { exec as execCallback } from "child_process";
|
|
|
1876
1876
|
import tar from "tar";
|
|
1877
1877
|
import os2 from "os";
|
|
1878
1878
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1879
|
+
async function acquireFileLock(lockPath, version) {
|
|
1880
|
+
const lockData = {
|
|
1881
|
+
version,
|
|
1882
|
+
pid: process.pid,
|
|
1883
|
+
timestamp: Date.now()
|
|
1884
|
+
};
|
|
1885
|
+
try {
|
|
1886
|
+
await fs2.writeFile(lockPath, JSON.stringify(lockData), { flag: "wx" });
|
|
1887
|
+
if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
|
|
1888
|
+
console.log(`Acquired file lock: ${lockPath}`);
|
|
1889
|
+
}
|
|
1890
|
+
return true;
|
|
1891
|
+
} catch (error) {
|
|
1892
|
+
if (error.code === "EEXIST") {
|
|
1893
|
+
try {
|
|
1894
|
+
const existingLock = JSON.parse(await fs2.readFile(lockPath, "utf-8"));
|
|
1895
|
+
const lockAge = Date.now() - existingLock.timestamp;
|
|
1896
|
+
if (lockAge > LOCK_TIMEOUT_MS) {
|
|
1897
|
+
if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
|
|
1898
|
+
console.log(`Removing stale lock file (age: ${Math.round(lockAge / 1e3)}s, pid: ${existingLock.pid})`);
|
|
1899
|
+
}
|
|
1900
|
+
await fs2.remove(lockPath);
|
|
1901
|
+
return false;
|
|
1902
|
+
}
|
|
1903
|
+
if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
|
|
1904
|
+
console.log(`Download in progress by process ${existingLock.pid}, waiting...`);
|
|
1905
|
+
}
|
|
1906
|
+
return false;
|
|
1907
|
+
} catch (readError) {
|
|
1908
|
+
if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
|
|
1909
|
+
console.log(`Lock file corrupted, removing: ${readError.message}`);
|
|
1910
|
+
}
|
|
1911
|
+
try {
|
|
1912
|
+
await fs2.remove(lockPath);
|
|
1913
|
+
} catch {
|
|
1914
|
+
}
|
|
1915
|
+
return false;
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1918
|
+
if (error.code === "EACCES" || error.code === "EPERM" || error.code === "EROFS") {
|
|
1919
|
+
if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
|
|
1920
|
+
console.log(`Cannot create lock file (${error.code}): ${lockPath}`);
|
|
1921
|
+
console.log(`File-based locking unavailable, will proceed without cross-process coordination`);
|
|
1922
|
+
}
|
|
1923
|
+
return null;
|
|
1924
|
+
}
|
|
1925
|
+
if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
|
|
1926
|
+
console.log(`Unexpected error creating lock file: ${error.message}`);
|
|
1927
|
+
console.log(`Proceeding without file-based lock`);
|
|
1928
|
+
}
|
|
1929
|
+
return null;
|
|
1930
|
+
}
|
|
1931
|
+
}
|
|
1932
|
+
async function releaseFileLock(lockPath) {
|
|
1933
|
+
try {
|
|
1934
|
+
await fs2.remove(lockPath);
|
|
1935
|
+
if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
|
|
1936
|
+
console.log(`Released file lock: ${lockPath}`);
|
|
1937
|
+
}
|
|
1938
|
+
} catch (error) {
|
|
1939
|
+
if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
|
|
1940
|
+
console.log(`Warning: Could not release lock file: ${error.message}`);
|
|
1941
|
+
}
|
|
1942
|
+
}
|
|
1943
|
+
}
|
|
1944
|
+
async function waitForFileLock(lockPath, binaryPath) {
|
|
1945
|
+
const startTime = Date.now();
|
|
1946
|
+
while (Date.now() - startTime < MAX_LOCK_WAIT_MS) {
|
|
1947
|
+
if (await fs2.pathExists(binaryPath)) {
|
|
1948
|
+
if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
|
|
1949
|
+
console.log(`Binary now available at ${binaryPath}, download completed by another process`);
|
|
1950
|
+
}
|
|
1951
|
+
return true;
|
|
1952
|
+
}
|
|
1953
|
+
const lockExists = await fs2.pathExists(lockPath);
|
|
1954
|
+
if (!lockExists) {
|
|
1955
|
+
if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
|
|
1956
|
+
console.log(`Lock file removed but binary not found - download may have failed`);
|
|
1957
|
+
}
|
|
1958
|
+
return false;
|
|
1959
|
+
}
|
|
1960
|
+
try {
|
|
1961
|
+
const lockData = JSON.parse(await fs2.readFile(lockPath, "utf-8"));
|
|
1962
|
+
const lockAge = Date.now() - lockData.timestamp;
|
|
1963
|
+
if (lockAge > LOCK_TIMEOUT_MS) {
|
|
1964
|
+
if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
|
|
1965
|
+
console.log(`Lock expired (age: ${Math.round(lockAge / 1e3)}s), will retry download`);
|
|
1966
|
+
}
|
|
1967
|
+
return false;
|
|
1968
|
+
}
|
|
1969
|
+
} catch {
|
|
1970
|
+
}
|
|
1971
|
+
await new Promise((resolve5) => setTimeout(resolve5, LOCK_POLL_INTERVAL_MS));
|
|
1972
|
+
}
|
|
1973
|
+
if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
|
|
1974
|
+
console.log(`Timeout waiting for file lock`);
|
|
1975
|
+
}
|
|
1976
|
+
return false;
|
|
1977
|
+
}
|
|
1978
|
+
async function withDownloadLock(version, downloadFn) {
|
|
1979
|
+
const lockKey = version || "latest";
|
|
1980
|
+
if (downloadLocks.has(lockKey)) {
|
|
1981
|
+
const lock = downloadLocks.get(lockKey);
|
|
1982
|
+
const lockAge = Date.now() - lock.timestamp;
|
|
1983
|
+
if (lockAge > LOCK_TIMEOUT_MS) {
|
|
1984
|
+
if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
|
|
1985
|
+
console.log(`In-memory lock for version ${lockKey} expired (age: ${Math.round(lockAge / 1e3)}s), removing stale lock`);
|
|
1986
|
+
}
|
|
1987
|
+
downloadLocks.delete(lockKey);
|
|
1988
|
+
} else {
|
|
1989
|
+
if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
|
|
1990
|
+
console.log(`Download already in progress in this process for version ${lockKey}, waiting...`);
|
|
1991
|
+
}
|
|
1992
|
+
try {
|
|
1993
|
+
return await lock.promise;
|
|
1994
|
+
} catch (error) {
|
|
1995
|
+
if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
|
|
1996
|
+
console.log(`In-memory locked download failed, will retry: ${error.message}`);
|
|
1997
|
+
}
|
|
1998
|
+
}
|
|
1999
|
+
}
|
|
2000
|
+
}
|
|
2001
|
+
const downloadPromise = Promise.race([
|
|
2002
|
+
downloadFn(),
|
|
2003
|
+
new Promise(
|
|
2004
|
+
(_, reject2) => setTimeout(() => reject2(new Error(`Download timeout after ${LOCK_TIMEOUT_MS / 1e3}s`)), LOCK_TIMEOUT_MS)
|
|
2005
|
+
)
|
|
2006
|
+
]);
|
|
2007
|
+
downloadLocks.set(lockKey, {
|
|
2008
|
+
promise: downloadPromise,
|
|
2009
|
+
timestamp: Date.now()
|
|
2010
|
+
});
|
|
2011
|
+
try {
|
|
2012
|
+
const result = await downloadPromise;
|
|
2013
|
+
return result;
|
|
2014
|
+
} finally {
|
|
2015
|
+
downloadLocks.delete(lockKey);
|
|
2016
|
+
}
|
|
2017
|
+
}
|
|
1879
2018
|
function detectOsArch() {
|
|
1880
2019
|
const osType = os2.platform();
|
|
1881
2020
|
const archType = os2.arch();
|
|
@@ -2319,16 +2458,64 @@ async function getPackageVersion() {
|
|
|
2319
2458
|
return "0.0.0";
|
|
2320
2459
|
}
|
|
2321
2460
|
}
|
|
2461
|
+
async function doDownload(version) {
|
|
2462
|
+
const localDir = await getPackageBinDir();
|
|
2463
|
+
if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
|
|
2464
|
+
console.log(`Downloading probe binary (version: ${version || "latest"})...`);
|
|
2465
|
+
console.log(`Using binary directory: ${localDir}`);
|
|
2466
|
+
}
|
|
2467
|
+
const isWindows = os2.platform() === "win32";
|
|
2468
|
+
const binaryName = isWindows ? `${BINARY_NAME}.exe` : `${BINARY_NAME}-binary`;
|
|
2469
|
+
const binaryPath = path2.join(localDir, binaryName);
|
|
2470
|
+
const { os: osInfo, arch: archInfo } = detectOsArch();
|
|
2471
|
+
let versionToUse = version;
|
|
2472
|
+
let bestAsset;
|
|
2473
|
+
let tagVersion;
|
|
2474
|
+
if (!versionToUse || versionToUse === "0.0.0") {
|
|
2475
|
+
if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
|
|
2476
|
+
console.log("No specific version requested, will use the latest release");
|
|
2477
|
+
}
|
|
2478
|
+
const { tag, assets } = await getLatestRelease(void 0);
|
|
2479
|
+
tagVersion = tag.startsWith("v") ? tag.substring(1) : tag;
|
|
2480
|
+
bestAsset = findBestAsset(assets, osInfo, archInfo);
|
|
2481
|
+
if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
|
|
2482
|
+
console.log(`Found release version: ${tagVersion}`);
|
|
2483
|
+
}
|
|
2484
|
+
} else {
|
|
2485
|
+
if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
|
|
2486
|
+
console.log(`Direct download for version: ${versionToUse}`);
|
|
2487
|
+
}
|
|
2488
|
+
tagVersion = versionToUse;
|
|
2489
|
+
bestAsset = constructAssetInfo(versionToUse, osInfo, archInfo);
|
|
2490
|
+
}
|
|
2491
|
+
const { assetPath, checksumPath } = await downloadAsset(bestAsset, localDir);
|
|
2492
|
+
const checksumValid = await verifyChecksum(assetPath, checksumPath);
|
|
2493
|
+
if (!checksumValid) {
|
|
2494
|
+
throw new Error("Checksum verification failed");
|
|
2495
|
+
}
|
|
2496
|
+
const extractedBinaryPath = await extractBinary(assetPath, localDir);
|
|
2497
|
+
await saveVersionInfo(tagVersion, localDir);
|
|
2498
|
+
try {
|
|
2499
|
+
await fs2.remove(assetPath);
|
|
2500
|
+
if (checksumPath) {
|
|
2501
|
+
await fs2.remove(checksumPath);
|
|
2502
|
+
}
|
|
2503
|
+
} catch (err) {
|
|
2504
|
+
if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
|
|
2505
|
+
console.log(`Warning: Could not clean up temporary files: ${err}`);
|
|
2506
|
+
}
|
|
2507
|
+
}
|
|
2508
|
+
if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
|
|
2509
|
+
console.log(`Binary successfully installed at ${extractedBinaryPath} (version: ${tagVersion})`);
|
|
2510
|
+
}
|
|
2511
|
+
return extractedBinaryPath;
|
|
2512
|
+
}
|
|
2322
2513
|
async function downloadProbeBinary(version) {
|
|
2323
2514
|
try {
|
|
2324
2515
|
const localDir = await getPackageBinDir();
|
|
2325
2516
|
if (!version || version === "0.0.0") {
|
|
2326
2517
|
version = await getPackageVersion();
|
|
2327
2518
|
}
|
|
2328
|
-
if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
|
|
2329
|
-
console.log(`Downloading probe binary (version: ${version || "latest"})...`);
|
|
2330
|
-
console.log(`Using binary directory: ${localDir}`);
|
|
2331
|
-
}
|
|
2332
2519
|
const isWindows = os2.platform() === "win32";
|
|
2333
2520
|
const binaryName = isWindows ? `${BINARY_NAME}.exe` : `${BINARY_NAME}-binary`;
|
|
2334
2521
|
const binaryPath = path2.join(localDir, binaryName);
|
|
@@ -2344,54 +2531,44 @@ async function downloadProbeBinary(version) {
|
|
|
2344
2531
|
console.log(`Existing binary version (${versionInfo?.version || "unknown"}) doesn't match requested version (${version}). Downloading new version...`);
|
|
2345
2532
|
}
|
|
2346
2533
|
}
|
|
2347
|
-
const
|
|
2348
|
-
|
|
2349
|
-
let
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
|
|
2359
|
-
console.log(`Found release version: ${tagVersion}`);
|
|
2534
|
+
const lockPath = path2.join(localDir, `.probe-download-${version}.lock`);
|
|
2535
|
+
const maxRetries = 3;
|
|
2536
|
+
for (let retry = 0; retry < maxRetries; retry++) {
|
|
2537
|
+
const lockAcquired = await acquireFileLock(lockPath, version);
|
|
2538
|
+
if (lockAcquired === true) {
|
|
2539
|
+
try {
|
|
2540
|
+
const result = await withDownloadLock(version, () => doDownload(version));
|
|
2541
|
+
return result;
|
|
2542
|
+
} finally {
|
|
2543
|
+
await releaseFileLock(lockPath);
|
|
2544
|
+
}
|
|
2360
2545
|
}
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2546
|
+
if (lockAcquired === null) {
|
|
2547
|
+
if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
|
|
2548
|
+
console.log(`File-based locking unavailable, downloading without cross-process coordination`);
|
|
2549
|
+
}
|
|
2550
|
+
return await withDownloadLock(version, () => doDownload(version));
|
|
2364
2551
|
}
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
const { assetPath, checksumPath } = await downloadAsset(bestAsset, localDir);
|
|
2369
|
-
const checksumValid = await verifyChecksum(assetPath, checksumPath);
|
|
2370
|
-
if (!checksumValid) {
|
|
2371
|
-
throw new Error("Checksum verification failed");
|
|
2372
|
-
}
|
|
2373
|
-
const extractedBinaryPath = await extractBinary(assetPath, localDir);
|
|
2374
|
-
await saveVersionInfo(tagVersion, localDir);
|
|
2375
|
-
try {
|
|
2376
|
-
await fs2.remove(assetPath);
|
|
2377
|
-
if (checksumPath) {
|
|
2378
|
-
await fs2.remove(checksumPath);
|
|
2552
|
+
const downloadCompleted = await waitForFileLock(lockPath, binaryPath);
|
|
2553
|
+
if (downloadCompleted) {
|
|
2554
|
+
return binaryPath;
|
|
2379
2555
|
}
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2556
|
+
if (retry < maxRetries - 1) {
|
|
2557
|
+
if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
|
|
2558
|
+
console.log(`Retrying download (attempt ${retry + 2}/${maxRetries})...`);
|
|
2559
|
+
}
|
|
2383
2560
|
}
|
|
2384
2561
|
}
|
|
2385
2562
|
if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
|
|
2386
|
-
console.log(`
|
|
2563
|
+
console.log(`All lock attempts exhausted, attempting direct download`);
|
|
2387
2564
|
}
|
|
2388
|
-
return
|
|
2565
|
+
return await withDownloadLock(version, () => doDownload(version));
|
|
2389
2566
|
} catch (error) {
|
|
2390
2567
|
console.error("Error downloading probe binary:", error);
|
|
2391
2568
|
throw error;
|
|
2392
2569
|
}
|
|
2393
2570
|
}
|
|
2394
|
-
var exec, REPO_OWNER, REPO_NAME, BINARY_NAME, __filename2, __dirname2;
|
|
2571
|
+
var exec, REPO_OWNER, REPO_NAME, BINARY_NAME, __filename2, __dirname2, downloadLocks, LOCK_TIMEOUT_MS, LOCK_POLL_INTERVAL_MS, MAX_LOCK_WAIT_MS;
|
|
2395
2572
|
var init_downloader = __esm({
|
|
2396
2573
|
"src/downloader.js"() {
|
|
2397
2574
|
"use strict";
|
|
@@ -2403,6 +2580,10 @@ var init_downloader = __esm({
|
|
|
2403
2580
|
BINARY_NAME = "probe";
|
|
2404
2581
|
__filename2 = fileURLToPath2(import.meta.url);
|
|
2405
2582
|
__dirname2 = path2.dirname(__filename2);
|
|
2583
|
+
downloadLocks = /* @__PURE__ */ new Map();
|
|
2584
|
+
LOCK_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
2585
|
+
LOCK_POLL_INTERVAL_MS = 1e3;
|
|
2586
|
+
MAX_LOCK_WAIT_MS = 5 * 60 * 1e3;
|
|
2406
2587
|
}
|
|
2407
2588
|
});
|
|
2408
2589
|
|
|
@@ -2412,13 +2593,15 @@ import fs3 from "fs-extra";
|
|
|
2412
2593
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
2413
2594
|
async function getBinaryPath(options = {}) {
|
|
2414
2595
|
const { forceDownload = false, version } = options;
|
|
2415
|
-
if (probeBinaryPath && !forceDownload && fs3.existsSync(probeBinaryPath)) {
|
|
2416
|
-
return probeBinaryPath;
|
|
2417
|
-
}
|
|
2418
2596
|
if (process.env.PROBE_PATH && fs3.existsSync(process.env.PROBE_PATH) && !forceDownload) {
|
|
2419
2597
|
probeBinaryPath = process.env.PROBE_PATH;
|
|
2420
2598
|
return probeBinaryPath;
|
|
2421
2599
|
}
|
|
2600
|
+
if (version && !forceDownload) {
|
|
2601
|
+
console.log(`Specific version ${version} requested. Downloading...`);
|
|
2602
|
+
probeBinaryPath = await downloadProbeBinary(version);
|
|
2603
|
+
return probeBinaryPath;
|
|
2604
|
+
}
|
|
2422
2605
|
const binDir = await getPackageBinDir();
|
|
2423
2606
|
const isWindows = process.platform === "win32";
|
|
2424
2607
|
const binaryName = isWindows ? "probe.exe" : "probe-binary";
|