UNPKG

@probelabs/probe

Version:

Node.js wrapper for the probe code search tool

1,302 lines (1,298 loc) 6.18 MB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __esm = (fn, res) => function __init() { return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res; }; var __commonJS = (cb, mod) => function __require() { return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; }; var __export = (target, all) => { for (var name14 in all) __defProp(target, name14, { get: all[name14], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // node_modules/dotenv/package.json var require_package = __commonJS({ "node_modules/dotenv/package.json"(exports2, module2) { module2.exports = { name: "dotenv", version: "16.6.1", description: "Loads environment variables from .env file", main: "lib/main.js", types: "lib/main.d.ts", exports: { ".": { types: "./lib/main.d.ts", require: "./lib/main.js", default: "./lib/main.js" }, "./config": "./config.js", "./config.js": "./config.js", "./lib/env-options": "./lib/env-options.js", "./lib/env-options.js": "./lib/env-options.js", "./lib/cli-options": "./lib/cli-options.js", "./lib/cli-options.js": "./lib/cli-options.js", "./package.json": "./package.json" }, scripts: { "dts-check": "tsc --project tests/types/tsconfig.json", lint: "standard", pretest: "npm run lint && npm run dts-check", test: "tap run --allow-empty-coverage --disable-coverage --timeout=60000", "test:coverage": "tap run --show-full-coverage --timeout=60000 --coverage-report=text --coverage-report=lcov", prerelease: "npm test", release: "standard-version" }, repository: { type: "git", url: "git://github.com/motdotla/dotenv.git" }, homepage: "https://github.com/motdotla/dotenv#readme", funding: "https://dotenvx.com", keywords: [ "dotenv", "env", ".env", "environment", "variables", "config", "settings" ], readmeFilename: "README.md", license: "BSD-2-Clause", devDependencies: { "@types/node": "^18.11.3", decache: "^4.6.2", sinon: "^14.0.1", standard: "^17.0.0", "standard-version": "^9.5.0", tap: "^19.2.0", typescript: "^4.8.4" }, engines: { node: ">=12" }, browser: { fs: false } }; } }); // node_modules/dotenv/lib/main.js var require_main = __commonJS({ "node_modules/dotenv/lib/main.js"(exports2, module2) { var fs7 = require("fs"); var path7 = require("path"); var os3 = require("os"); var crypto2 = require("crypto"); var packageJson = require_package(); var version = packageJson.version; var LINE = /(?:^|^)\s*(?:export\s+)?([\w.-]+)(?:\s*=\s*?|:\s+?)(\s*'(?:\\'|[^'])*'|\s*"(?:\\"|[^"])*"|\s*`(?:\\`|[^`])*`|[^#\r\n]+)?\s*(?:#.*)?(?:$|$)/mg; function parse6(src) { const obj = {}; let lines = src.toString(); lines = lines.replace(/\r\n?/mg, "\n"); let match2; while ((match2 = LINE.exec(lines)) != null) { const key = match2[1]; let value = match2[2] || ""; value = value.trim(); const maybeQuote = value[0]; value = value.replace(/^(['"`])([\s\S]*)\1$/mg, "$2"); if (maybeQuote === '"') { value = value.replace(/\\n/g, "\n"); value = value.replace(/\\r/g, "\r"); } obj[key] = value; } return obj; } function _parseVault(options) { options = options || {}; const vaultPath = _vaultPath(options); options.path = vaultPath; const result = DotenvModule.configDotenv(options); if (!result.parsed) { const err = new Error(`MISSING_DATA: Cannot parse ${vaultPath} for an unknown reason`); err.code = "MISSING_DATA"; throw err; } const keys2 = _dotenvKey(options).split(","); const length = keys2.length; let decrypted; for (let i3 = 0; i3 < length; i3++) { try { const key = keys2[i3].trim(); const attrs = _instructions(result, key); decrypted = DotenvModule.decrypt(attrs.ciphertext, attrs.key); break; } catch (error2) { if (i3 + 1 >= length) { throw error2; } } } return DotenvModule.parse(decrypted); } function _warn(message) { console.log(`[dotenv@${version}][WARN] ${message}`); } function _debug(message) { console.log(`[dotenv@${version}][DEBUG] ${message}`); } function _log(message) { console.log(`[dotenv@${version}] ${message}`); } function _dotenvKey(options) { if (options && options.DOTENV_KEY && options.DOTENV_KEY.length > 0) { return options.DOTENV_KEY; } if (process.env.DOTENV_KEY && process.env.DOTENV_KEY.length > 0) { return process.env.DOTENV_KEY; } return ""; } function _instructions(result, dotenvKey) { let uri; try { uri = new URL(dotenvKey); } catch (error2) { if (error2.code === "ERR_INVALID_URL") { const err = new Error("INVALID_DOTENV_KEY: Wrong format. Must be in valid uri format like dotenv://:key_1234@dotenvx.com/vault/.env.vault?environment=development"); err.code = "INVALID_DOTENV_KEY"; throw err; } throw error2; } const key = uri.password; if (!key) { const err = new Error("INVALID_DOTENV_KEY: Missing key part"); err.code = "INVALID_DOTENV_KEY"; throw err; } const environment = uri.searchParams.get("environment"); if (!environment) { const err = new Error("INVALID_DOTENV_KEY: Missing environment part"); err.code = "INVALID_DOTENV_KEY"; throw err; } const environmentKey = `DOTENV_VAULT_${environment.toUpperCase()}`; const ciphertext = result.parsed[environmentKey]; if (!ciphertext) { const err = new Error(`NOT_FOUND_DOTENV_ENVIRONMENT: Cannot locate environment ${environmentKey} in your .env.vault file.`); err.code = "NOT_FOUND_DOTENV_ENVIRONMENT"; throw err; } return { ciphertext, key }; } function _vaultPath(options) { let possibleVaultPath = null; if (options && options.path && options.path.length > 0) { if (Array.isArray(options.path)) { for (const filepath of options.path) { if (fs7.existsSync(filepath)) { possibleVaultPath = filepath.endsWith(".vault") ? filepath : `${filepath}.vault`; } } } else { possibleVaultPath = options.path.endsWith(".vault") ? options.path : `${options.path}.vault`; } } else { possibleVaultPath = path7.resolve(process.cwd(), ".env.vault"); } if (fs7.existsSync(possibleVaultPath)) { return possibleVaultPath; } return null; } function _resolveHome(envPath) { return envPath[0] === "~" ? path7.join(os3.homedir(), envPath.slice(1)) : envPath; } function _configVault(options) { const debug = Boolean(options && options.debug); const quiet = options && "quiet" in options ? options.quiet : true; if (debug || !quiet) { _log("Loading env from encrypted .env.vault"); } const parsed = DotenvModule._parseVault(options); let processEnv = process.env; if (options && options.processEnv != null) { processEnv = options.processEnv; } DotenvModule.populate(processEnv, parsed, options); return { parsed }; } function configDotenv(options) { const dotenvPath = path7.resolve(process.cwd(), ".env"); let encoding = "utf8"; const debug = Boolean(options && options.debug); const quiet = options && "quiet" in options ? options.quiet : true; if (options && options.encoding) { encoding = options.encoding; } else { if (debug) { _debug("No encoding is specified. UTF-8 is used by default"); } } let optionPaths = [dotenvPath]; if (options && options.path) { if (!Array.isArray(options.path)) { optionPaths = [_resolveHome(options.path)]; } else { optionPaths = []; for (const filepath of options.path) { optionPaths.push(_resolveHome(filepath)); } } } let lastError; const parsedAll = {}; for (const path8 of optionPaths) { try { const parsed = DotenvModule.parse(fs7.readFileSync(path8, { encoding })); DotenvModule.populate(parsedAll, parsed, options); } catch (e3) { if (debug) { _debug(`Failed to load ${path8} ${e3.message}`); } lastError = e3; } } let processEnv = process.env; if (options && options.processEnv != null) { processEnv = options.processEnv; } DotenvModule.populate(processEnv, parsedAll, options); if (debug || !quiet) { const keysCount = Object.keys(parsedAll).length; const shortPaths = []; for (const filePath of optionPaths) { try { const relative = path7.relative(process.cwd(), filePath); shortPaths.push(relative); } catch (e3) { if (debug) { _debug(`Failed to load ${filePath} ${e3.message}`); } lastError = e3; } } _log(`injecting env (${keysCount}) from ${shortPaths.join(",")}`); } if (lastError) { return { parsed: parsedAll, error: lastError }; } else { return { parsed: parsedAll }; } } function config(options) { if (_dotenvKey(options).length === 0) { return DotenvModule.configDotenv(options); } const vaultPath = _vaultPath(options); if (!vaultPath) { _warn(`You set DOTENV_KEY but you are missing a .env.vault file at ${vaultPath}. Did you forget to build it?`); return DotenvModule.configDotenv(options); } return DotenvModule._configVault(options); } function decrypt(encrypted, keyStr) { const key = Buffer.from(keyStr.slice(-64), "hex"); let ciphertext = Buffer.from(encrypted, "base64"); const nonce = ciphertext.subarray(0, 12); const authTag = ciphertext.subarray(-16); ciphertext = ciphertext.subarray(12, -16); try { const aesgcm = crypto2.createDecipheriv("aes-256-gcm", key, nonce); aesgcm.setAuthTag(authTag); return `${aesgcm.update(ciphertext)}${aesgcm.final()}`; } catch (error2) { const isRange = error2 instanceof RangeError; const invalidKeyLength = error2.message === "Invalid key length"; const decryptionFailed = error2.message === "Unsupported state or unable to authenticate data"; if (isRange || invalidKeyLength) { const err = new Error("INVALID_DOTENV_KEY: It must be 64 characters long (or more)"); err.code = "INVALID_DOTENV_KEY"; throw err; } else if (decryptionFailed) { const err = new Error("DECRYPTION_FAILED: Please check your DOTENV_KEY"); err.code = "DECRYPTION_FAILED"; throw err; } else { throw error2; } } } function populate(processEnv, parsed, options = {}) { const debug = Boolean(options && options.debug); const override = Boolean(options && options.override); if (typeof parsed !== "object") { const err = new Error("OBJECT_REQUIRED: Please check the processEnv argument being passed to populate"); err.code = "OBJECT_REQUIRED"; throw err; } for (const key of Object.keys(parsed)) { if (Object.prototype.hasOwnProperty.call(processEnv, key)) { if (override === true) { processEnv[key] = parsed[key]; } if (debug) { if (override === true) { _debug(`"${key}" is already defined and WAS overwritten`); } else { _debug(`"${key}" is already defined and was NOT overwritten`); } } } else { processEnv[key] = parsed[key]; } } } var DotenvModule = { configDotenv, _configVault, _parseVault, config, decrypt, parse: parse6, populate }; module2.exports.configDotenv = DotenvModule.configDotenv; module2.exports._configVault = DotenvModule._configVault; module2.exports._parseVault = DotenvModule._parseVault; module2.exports.config = DotenvModule.config; module2.exports.decrypt = DotenvModule.decrypt; module2.exports.parse = DotenvModule.parse; module2.exports.populate = DotenvModule.populate; module2.exports = DotenvModule; } }); // src/directory-resolver.js async function getPackageBinDir() { const debug = process.env.DEBUG === "1" || process.env.VERBOSE === "1"; if (debug) { console.log("DEBUG: Starting probe binary directory resolution"); } if (process.env.PROBE_BINARY_PATH) { if (debug) { console.log(`DEBUG: Checking PROBE_BINARY_PATH: ${process.env.PROBE_BINARY_PATH}`); } const binaryPath = process.env.PROBE_BINARY_PATH; if (await import_fs_extra.default.pathExists(binaryPath)) { const binDir = import_path.default.dirname(binaryPath); if (await canWriteToDirectory(binDir)) { if (debug) { console.log(`DEBUG: Using PROBE_BINARY_PATH directory: ${binDir}`); } return binDir; } } else { console.warn(`Warning: PROBE_BINARY_PATH ${binaryPath} does not exist`); } } if (process.env.PROBE_CACHE_DIR) { if (debug) { console.log(`DEBUG: Checking PROBE_CACHE_DIR: ${process.env.PROBE_CACHE_DIR}`); } const cacheDir = import_path.default.join(process.env.PROBE_CACHE_DIR, "bin"); if (await ensureDirectory(cacheDir)) { if (debug) { console.log(`DEBUG: Using PROBE_CACHE_DIR: ${cacheDir}`); } return cacheDir; } } const packageRoot = await findPackageRoot(); if (packageRoot) { if (debug) { console.log(`DEBUG: Found package root: ${packageRoot}`); } const packageBinDir = import_path.default.join(packageRoot, "bin"); if (await ensureDirectory(packageBinDir) && await canWriteToDirectory(packageBinDir)) { if (debug) { console.log(`DEBUG: Using package bin directory: ${packageBinDir}`); } return packageBinDir; } else if (debug) { console.log(`DEBUG: Package bin directory ${packageBinDir} not writable, trying fallbacks`); } } const homeCache = import_path.default.join(import_os.default.homedir(), ".probe", "bin"); if (debug) { console.log(`DEBUG: Trying home cache directory: ${homeCache}`); } if (await ensureDirectory(homeCache)) { if (debug) { console.log(`DEBUG: Using home cache directory: ${homeCache}`); } return homeCache; } const tempCache = import_path.default.join(import_os.default.tmpdir(), "probe-cache", "bin"); if (debug) { console.log(`DEBUG: Trying temp cache directory: ${tempCache}`); } if (await ensureDirectory(tempCache)) { if (debug) { console.log(`DEBUG: Using temp cache directory: ${tempCache}`); } return tempCache; } const errorMessage = [ "Could not find a writable directory for probe binary.", "Tried the following locations:", packageRoot ? `- Package bin directory: ${import_path.default.join(packageRoot, "bin")}` : "- Package root not found", `- Home cache directory: ${homeCache}`, `- Temp cache directory: ${tempCache}`, "", "You can override the location using environment variables:", "- PROBE_BINARY_PATH=/path/to/probe (direct path to binary)", "- PROBE_CACHE_DIR=/path/to/cache (cache directory, binary will be in /bin subdirectory)" ].join("\n"); throw new Error(errorMessage); } async function findPackageRoot() { const debug = process.env.DEBUG === "1" || process.env.VERBOSE === "1"; let currentDir = __dirname; const rootDir = import_path.default.parse(currentDir).root; if (debug) { console.log(`DEBUG: Starting package root search from: ${currentDir}`); } while (currentDir !== rootDir) { const packageJsonPath = import_path.default.join(currentDir, "package.json"); try { if (await import_fs_extra.default.pathExists(packageJsonPath)) { const packageJson = await import_fs_extra.default.readJson(packageJsonPath); if (debug) { console.log(`DEBUG: Found package.json at ${packageJsonPath}, name: ${packageJson.name}`); } if (packageJson.name === "@probelabs/probe") { if (debug) { console.log(`DEBUG: Found probe package root: ${currentDir}`); } return currentDir; } } } catch (err) { if (debug) { console.log(`DEBUG: Error reading package.json at ${packageJsonPath}: ${err.message}`); } } currentDir = import_path.default.dirname(currentDir); } if (debug) { console.log("DEBUG: Package root not found, reached filesystem root"); } return null; } async function ensureDirectory(dirPath) { const debug = process.env.DEBUG === "1" || process.env.VERBOSE === "1"; try { await import_fs_extra.default.ensureDir(dirPath); const testFile = import_path.default.join(dirPath, ".probe-write-test"); await import_fs_extra.default.writeFile(testFile, "test"); await import_fs_extra.default.remove(testFile); if (debug) { console.log(`DEBUG: Directory ${dirPath} is writable`); } return true; } catch (error2) { if (debug) { console.log(`DEBUG: Directory ${dirPath} not writable: ${error2.message}`); } return false; } } async function canWriteToDirectory(dirPath) { const debug = process.env.DEBUG === "1" || process.env.VERBOSE === "1"; try { const exists = await import_fs_extra.default.pathExists(dirPath); if (!exists) { if (debug) { console.log(`DEBUG: Directory ${dirPath} does not exist`); } return false; } const testFile = import_path.default.join(dirPath, ".probe-write-test"); await import_fs_extra.default.writeFile(testFile, "test"); await import_fs_extra.default.remove(testFile); if (debug) { console.log(`DEBUG: Directory ${dirPath} is writable`); } return true; } catch (error2) { if (debug) { console.log(`DEBUG: Directory ${dirPath} not writable: ${error2.message}`); } return false; } } var import_path, import_os, import_fs_extra, import_url, __filename, __dirname; var init_directory_resolver = __esm({ "src/directory-resolver.js"() { "use strict"; import_path = __toESM(require("path"), 1); import_os = __toESM(require("os"), 1); import_fs_extra = __toESM(require("fs-extra"), 1); import_url = require("url"); __filename = (0, import_url.fileURLToPath)("file:///"); __dirname = import_path.default.dirname(__filename); } }); // src/downloader.js function sanitizeError(err) { try { const status = err?.response?.status; const statusText = err?.response?.statusText; const url = err?.config?.url || err?.response?.config?.url; const code = err?.code; const serverMsg = typeof err?.response?.data === "string" ? err.response.data.slice(0, 500) : typeof err?.response?.data?.message === "string" ? err.response.data.message : void 0; const base = err?.message || String(err); const parts = [base]; if (status || statusText) parts.push(`[${status ?? ""} ${statusText ?? ""}]`.trim()); if (code) parts.push(`code=${code}`); if (url) parts.push(`url=${url}`); if (serverMsg) parts.push(`server="${String(serverMsg).replace(/\s+/g, " ").trim()}"`); const e3 = new Error(parts.filter(Boolean).join(" ")); if (status) e3.status = status; if (code) e3.code = code; if (url) e3.url = url; return e3; } catch (_2) { return new Error(err?.message || String(err)); } } async function acquireFileLock(lockPath, version) { const lockData = { version, pid: process.pid, timestamp: Date.now() }; try { await import_fs_extra2.default.writeFile(lockPath, JSON.stringify(lockData), { flag: "wx" }); if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(`Acquired file lock: ${lockPath}`); } return true; } catch (error2) { if (error2.code === "EEXIST") { try { const existingLock = JSON.parse(await import_fs_extra2.default.readFile(lockPath, "utf-8")); const lockAge = Date.now() - existingLock.timestamp; if (lockAge > LOCK_TIMEOUT_MS) { if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(`Removing stale lock file (age: ${Math.round(lockAge / 1e3)}s, pid: ${existingLock.pid})`); } await import_fs_extra2.default.remove(lockPath); return false; } if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(`Download in progress by process ${existingLock.pid}, waiting...`); } return false; } catch (readError) { if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(`Lock file corrupted, removing: ${readError.message}`); } try { await import_fs_extra2.default.remove(lockPath); } catch { } return false; } } if (error2.code === "EACCES" || error2.code === "EPERM" || error2.code === "EROFS") { if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(`Cannot create lock file (${error2.code}): ${lockPath}`); console.log(`File-based locking unavailable, will proceed without cross-process coordination`); } return null; } if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(`Unexpected error creating lock file: ${error2.message}`); console.log(`Proceeding without file-based lock`); } return null; } } async function releaseFileLock(lockPath) { try { await import_fs_extra2.default.remove(lockPath); if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(`Released file lock: ${lockPath}`); } } catch (error2) { if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(`Warning: Could not release lock file: ${error2.message}`); } } } async function waitForFileLock(lockPath, binaryPath) { const startTime = Date.now(); while (Date.now() - startTime < MAX_LOCK_WAIT_MS) { if (await import_fs_extra2.default.pathExists(binaryPath)) { if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(`Binary now available at ${binaryPath}, download completed by another process`); } return true; } const lockExists = await import_fs_extra2.default.pathExists(lockPath); if (!lockExists) { if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(`Lock file removed but binary not found - download may have failed`); } return false; } try { const lockData = JSON.parse(await import_fs_extra2.default.readFile(lockPath, "utf-8")); const lockAge = Date.now() - lockData.timestamp; if (lockAge > LOCK_TIMEOUT_MS) { if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(`Lock expired (age: ${Math.round(lockAge / 1e3)}s), will retry download`); } return false; } } catch { } await new Promise((resolve5) => setTimeout(resolve5, LOCK_POLL_INTERVAL_MS)); } if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(`Timeout waiting for file lock`); } return false; } async function withDownloadLock(version, downloadFn) { const lockKey = version || "latest"; if (downloadLocks.has(lockKey)) { const lock = downloadLocks.get(lockKey); const lockAge = Date.now() - lock.timestamp; if (lockAge > LOCK_TIMEOUT_MS) { if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(`In-memory lock for version ${lockKey} expired (age: ${Math.round(lockAge / 1e3)}s), removing stale lock`); } downloadLocks.delete(lockKey); } else { if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(`Download already in progress in this process for version ${lockKey}, waiting...`); } try { return await lock.promise; } catch (error2) { if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(`In-memory locked download failed, will retry: ${error2.message}`); } } } } const downloadPromise = Promise.race([ downloadFn(), new Promise( (_2, reject2) => setTimeout(() => reject2(new Error(`Download timeout after ${LOCK_TIMEOUT_MS / 1e3}s`)), LOCK_TIMEOUT_MS) ) ]); downloadLocks.set(lockKey, { promise: downloadPromise, timestamp: Date.now() }); try { const result = await downloadPromise; return result; } finally { downloadLocks.delete(lockKey); } } function detectOsArch() { const osType = import_os2.default.platform(); const archType = import_os2.default.arch(); let osInfo; let archInfo; switch (osType) { case "linux": osInfo = { type: "linux", keywords: ["linux", "Linux", "musl", "gnu"] }; break; case "darwin": osInfo = { type: "darwin", keywords: ["darwin", "Darwin", "mac", "Mac", "apple", "Apple", "osx", "OSX"] }; break; case "win32": osInfo = { type: "windows", keywords: ["windows", "Windows", "msvc", "pc-windows"] }; break; default: throw new Error(`Unsupported operating system: ${osType}`); } switch (archType) { case "x64": archInfo = { type: "x86_64", keywords: ["x86_64", "amd64", "x64", "64bit", "64-bit"] }; break; case "arm64": archInfo = { type: "aarch64", keywords: ["arm64", "aarch64", "arm", "ARM"] }; break; default: throw new Error(`Unsupported architecture: ${archType}`); } if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(`Detected OS: ${osInfo.type}, Architecture: ${archInfo.type}`); } return { os: osInfo, arch: archInfo }; } function constructAssetInfo(version, osInfo, archInfo) { let platform; let extension; switch (osInfo.type) { case "linux": platform = `${archInfo.type}-unknown-linux-musl`; extension = "tar.gz"; break; case "darwin": platform = `${archInfo.type}-apple-darwin`; extension = "tar.gz"; break; case "windows": platform = `${archInfo.type}-pc-windows-msvc`; extension = "zip"; break; default: throw new Error(`Unsupported OS type: ${osInfo.type}`); } const assetName = `probe-v${version}-${platform}.${extension}`; const checksumName = `${assetName}.sha256`; const baseUrl = `https://github.com/${REPO_OWNER}/${REPO_NAME}/releases/download/v${version}`; const assetUrl = `${baseUrl}/${assetName}`; const checksumUrl = `${baseUrl}/${checksumName}`; if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(`Constructed asset URL: ${assetUrl}`); } return { name: assetName, url: assetUrl, checksumName, checksumUrl }; } async function getLatestRelease(version) { if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log("Fetching release information..."); } try { let releaseUrl; if (version) { releaseUrl = `https://api.github.com/repos/${REPO_OWNER}/${REPO_NAME}/releases/tags/v${version}`; } else { releaseUrl = `https://api.github.com/repos/${REPO_OWNER}/${REPO_NAME}/releases`; } const response = await import_axios.default.get(releaseUrl); if (response.status !== 200) { throw new Error(`Failed to fetch release information: ${response.statusText}`); } let releaseData; if (version) { releaseData = response.data; } else { if (!Array.isArray(response.data) || response.data.length === 0) { throw new Error("No releases found"); } releaseData = response.data[0]; } const tag2 = releaseData.tag_name; const assets = releaseData.assets.map((asset) => ({ name: asset.name, url: asset.browser_download_url })); if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(`Found release: ${tag2} with ${assets.length} assets`); } return { tag: tag2, assets }; } catch (error2) { if (import_axios.default.isAxiosError(error2) && error2.response?.status === 404) { if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(`Release v${version} not found, trying to fetch all releases...`); } const response = await import_axios.default.get(`https://api.github.com/repos/${REPO_OWNER}/${REPO_NAME}/releases`); if (response.data.length === 0) { throw new Error("No releases found"); } let bestRelease = response.data[0]; if (version && version !== "0.0.0") { if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(`Looking for releases matching version: ${version}`); console.log(`Available releases: ${response.data.slice(0, 5).map((r3) => r3.tag_name).join(", ")}...`); } for (const release of response.data) { const releaseTag = release.tag_name.startsWith("v") ? release.tag_name.substring(1) : release.tag_name; if (releaseTag === version) { if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(`Found exact matching release: ${release.tag_name}`); } bestRelease = release; break; } } if (bestRelease === response.data[0]) { const versionParts = version.split(/[\.-]/); const majorMinor = versionParts.slice(0, 2).join("."); if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(`Looking for releases matching major.minor: ${majorMinor}`); } for (const release of response.data) { const releaseTag = release.tag_name.startsWith("v") ? release.tag_name.substring(1) : release.tag_name; const releaseVersionParts = releaseTag.split(/[\.-]/); const releaseMajorMinor = releaseVersionParts.slice(0, 2).join("."); if (releaseMajorMinor === majorMinor) { if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(`Found matching major.minor release: ${release.tag_name}`); } bestRelease = release; break; } } } } const tag2 = bestRelease.tag_name; const assets = bestRelease.assets.map((asset) => ({ name: asset.name, url: asset.browser_download_url })); if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(`Using release: ${tag2} with ${assets.length} assets`); } return { tag: tag2, assets }; } throw sanitizeError(error2); } } function findBestAsset(assets, osInfo, archInfo) { if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(`Finding appropriate binary for ${osInfo.type} ${archInfo.type}...`); } let bestAsset = null; let bestScore = 0; for (const asset of assets) { if (asset.name.endsWith(".sha256") || asset.name.endsWith(".md5") || asset.name.endsWith(".asc")) { continue; } if (osInfo.type === "windows" && asset.name.match(/darwin|linux/)) { if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(`Skipping non-Windows binary: ${asset.name}`); } continue; } else if (osInfo.type === "darwin" && asset.name.match(/windows|msvc|linux/)) { if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(`Skipping non-macOS binary: ${asset.name}`); } continue; } else if (osInfo.type === "linux" && asset.name.match(/darwin|windows|msvc/)) { if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(`Skipping non-Linux binary: ${asset.name}`); } continue; } let score = 0; if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(`Evaluating asset: ${asset.name}`); } let osMatched = false; for (const keyword of osInfo.keywords) { if (asset.name.includes(keyword)) { score += 10; osMatched = true; if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(` OS match found (${keyword}): +10, score = ${score}`); } break; } } for (const keyword of archInfo.keywords) { if (asset.name.includes(keyword)) { score += 5; if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(` Arch match found (${keyword}): +5, score = ${score}`); } break; } } if (asset.name.startsWith(`${BINARY_NAME}-`)) { score += 3; if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(` Binary name match: +3, score = ${score}`); } } if (osMatched && score >= 15) { score += 5; if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(` OS+Arch bonus: +5, score = ${score}`); } } if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(` Final score for ${asset.name}: ${score}`); } if (score === 23) { if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(`Found perfect match: ${asset.name}`); } return asset; } if (score > bestScore) { bestScore = score; bestAsset = asset; if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(` New best asset: ${asset.name} (score: ${score})`); } } } if (!bestAsset) { throw new Error(`Could not find a suitable binary for ${osInfo.type} ${archInfo.type}`); } if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(`Selected asset: ${bestAsset.name} (score: ${bestScore})`); } return bestAsset; } async function downloadAsset(asset, outputDir) { await import_fs_extra2.default.ensureDir(outputDir); const assetPath = import_path2.default.join(outputDir, asset.name); if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(`Downloading ${asset.name}...`); } const assetResponse = await import_axios.default.get(asset.url, { responseType: "arraybuffer" }); await import_fs_extra2.default.writeFile(assetPath, Buffer.from(assetResponse.data)); const checksumUrl = asset.checksumUrl || `${asset.url}.sha256`; const checksumFileName = asset.checksumName || `${asset.name}.sha256`; let checksumPath = null; try { if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(`Downloading checksum...`); } const checksumResponse = await import_axios.default.get(checksumUrl); checksumPath = import_path2.default.join(outputDir, checksumFileName); await import_fs_extra2.default.writeFile(checksumPath, checksumResponse.data); } catch (error2) { if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log("No checksum file found, skipping verification"); } } return { assetPath, checksumPath }; } async function verifyChecksum(assetPath, checksumPath) { if (!checksumPath) { return true; } if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(`Verifying checksum...`); } const checksumContent = await import_fs_extra2.default.readFile(checksumPath, "utf-8"); const expectedChecksum = checksumContent.trim().split(" ")[0]; const fileBuffer = await import_fs_extra2.default.readFile(assetPath); const actualChecksum = (0, import_crypto.createHash)("sha256").update(fileBuffer).digest("hex"); if (expectedChecksum !== actualChecksum) { console.error(`Checksum verification failed!`); console.error(`Expected: ${expectedChecksum}`); console.error(`Actual: ${actualChecksum}`); return false; } if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(`Checksum verified successfully`); } return true; } async function extractBinary(assetPath, outputDir) { if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(`Extracting ${import_path2.default.basename(assetPath)}...`); } const assetName = import_path2.default.basename(assetPath); const isWindows = import_os2.default.platform() === "win32"; const binaryName = isWindows ? `${BINARY_NAME}.exe` : `${BINARY_NAME}-binary`; const binaryPath = import_path2.default.join(outputDir, binaryName); try { const extractDir = import_path2.default.join(outputDir, "temp_extract"); await import_fs_extra2.default.ensureDir(extractDir); if (assetName.endsWith(".tar.gz") || assetName.endsWith(".tgz")) { if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(`Extracting tar.gz to ${extractDir}...`); } await import_tar.default.extract({ file: assetPath, cwd: extractDir }); } else if (assetName.endsWith(".zip")) { if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(`Extracting zip to ${extractDir}...`); } await exec(`unzip -q "${assetPath}" -d "${extractDir}"`); } else { if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(`Copying binary directly to ${binaryPath}`); } await import_fs_extra2.default.copyFile(assetPath, binaryPath); if (!isWindows) { await import_fs_extra2.default.chmod(binaryPath, 493); } await import_fs_extra2.default.remove(extractDir); if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(`Binary installed to ${binaryPath}`); } return binaryPath; } if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(`Searching for binary in extracted files...`); } const findBinary = async (dir) => { const entries = await import_fs_extra2.default.readdir(dir, { withFileTypes: true }); for (const entry of entries) { const fullPath = import_path2.default.join(dir, entry.name); if (entry.isDirectory()) { const result = await findBinary(fullPath); if (result) return result; } else if (entry.isFile()) { if (entry.name === binaryName || entry.name === BINARY_NAME || isWindows && entry.name.endsWith(".exe")) { return fullPath; } } } return null; }; const binaryFilePath = await findBinary(extractDir); if (!binaryFilePath) { const allFiles = await import_fs_extra2.default.readdir(extractDir, { recursive: true }); console.error(`Binary not found in extracted files. Found: ${allFiles.join(", ")}`); throw new Error(`Binary not found in the archive.`); } if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(`Found binary at ${binaryFilePath}`); console.log(`Copying binary to ${binaryPath}`); } await import_fs_extra2.default.copyFile(binaryFilePath, binaryPath); if (!isWindows) { await import_fs_extra2.default.chmod(binaryPath, 493); } await import_fs_extra2.default.remove(extractDir); if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(`Binary successfully installed to ${binaryPath}`); } return binaryPath; } catch (error2) { console.error(`Error extracting binary: ${error2 instanceof Error ? error2.message : String(error2)}`); throw sanitizeError(error2); } } async function getVersionInfo(binDir) { try { const versionInfoPath = import_path2.default.join(binDir, "version-info.json"); if (await import_fs_extra2.default.pathExists(versionInfoPath)) { const content = await import_fs_extra2.default.readFile(versionInfoPath, "utf-8"); return JSON.parse(content); } return null; } catch (error2) { console.warn(`Warning: Could not read version info: ${error2}`); return null; } } async function saveVersionInfo(version, binDir) { const versionInfo = { version, lastUpdated: (/* @__PURE__ */ new Date()).toISOString() }; const versionInfoPath = import_path2.default.join(binDir, "version-info.json"); await import_fs_extra2.default.writeFile(versionInfoPath, JSON.stringify(versionInfo, null, 2)); if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(`Version info saved: ${version} at ${versionInfoPath}`); } } async function getPackageVersion() { try { const possiblePaths = [ import_path2.default.resolve(__dirname2, "..", "package.json"), // When installed from npm: src/../package.json import_path2.default.resolve(__dirname2, "..", "..", "package.json") // In development: src/../../package.json ]; for (const packageJsonPath of possiblePaths) { try { if (import_fs_extra2.default.existsSync(packageJsonPath)) { if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(`Found package.json at: ${packageJsonPath}`); } const packageJson = JSON.parse(import_fs_extra2.default.readFileSync(packageJsonPath, "utf-8")); if (packageJson.version) { if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(`Using version from package.json: ${packageJson.version}`); } return packageJson.version; } } } catch (err) { console.error(`Error reading package.json at ${packageJsonPath}:`, err); } } return "0.0.0"; } catch (error2) { console.error("Error getting package version:", error2); return "0.0.0"; } } async function doDownload(version) { const localDir = await getPackageBinDir(); if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(`Downloading probe binary (version: ${version || "latest"})...`); console.log(`Using binary directory: ${localDir}`); } const isWindows = import_os2.default.platform() === "win32"; const binaryName = isWindows ? `${BINARY_NAME}.exe` : `${BINARY_NAME}-binary`; const binaryPath = import_path2.default.join(localDir, binaryName); const { os: osInfo, arch: archInfo } = detectOsArch(); let versionToUse = version; let bestAsset; let tagVersion; if (!versionToUse || versionToUse === "0.0.0") { if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log("No specific version requested, will use the latest release"); } const { tag: tag2, assets } = await getLatestRelease(void 0); tagVersion = tag2.startsWith("v") ? tag2.substring(1) : tag2; bestAsset = findBestAsset(assets, osInfo, archInfo); if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(`Found release version: ${tagVersion}`); } } else { if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(`Direct download for version: ${versionToUse}`); } tagVersion = versionToUse; bestAsset = constructAssetInfo(versionToUse, osInfo, archInfo); } const { assetPath, checksumPath } = await downloadAsset(bestAsset, localDir); const checksumValid = await verifyChecksum(assetPath, checksumPath); if (!checksumValid) { throw new Error("Checksum verification failed"); } const extractedBinaryPath = await extractBinary(assetPath, localDir); await saveVersionInfo(tagVersion, localDir); try { await import_fs_extra2.default.remove(assetPath); if (checksumPath) { await import_fs_extra2.default.remove(checksumPath); } } catch (err) { if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(`Warning: Could not clean up temporary files: ${err}`); } } if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(`Binary successfully installed at ${extractedBinaryPath} (version: ${tagVersion})`); } return extractedBinaryPath; } async function downloadProbeBinary(version) { try { const localDir = await getPackageBinDir(); if (!version || version === "0.0.0") { version = await getPackageVersion(); } const isWindows = import_os2.default.platform() === "win32"; const binaryName = isWindows ? `${BINARY_NAME}.exe` : `${BINARY_NAME}-binary`; const binaryPath = import_path2.default.join(localDir, binaryName); if (await import_fs_extra2.default.pathExists(binaryPath)) { const versionInfo = await getVersionInfo(localDir); if (versionInfo && versionInfo.version === version) { if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(`Using existing binary at ${binaryPath} (version: ${versionInfo.version})`); } return binaryPath; } if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(`Existing binary version (${versionInfo?.version || "unknown"}) doesn't match requested version (${version}). Downloading new version...`); } } const lockPath = import_path2.default.join(localDir, `.probe-download-${version}.lock`); const maxRetries = 3; for (let retry = 0; retry < maxRetries; retry++) { const lockAcquired = await acquireFileLock(lockPath, version); if (lockAcquired === true) { try { const result = await withDownloadLock(version, () => doDownload(version)); return result; } finally { await releaseFileLock(lockPath); } } if (lockAcquired === null) { if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(`File-based locking unavailable, downloading without cross-process coordination`); } return await withDownloadLock(version, () => doDownload(version)); } const downloadCompleted = await waitForFileLock(lockPath, binaryPath); if (downloadCompleted) { return binaryPath; } if (retry < maxRetries - 1) { if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(`Retrying download (attempt ${retry + 2}/${maxRetries})...`); } } } if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") { console.log(`All lock attempts exhausted, attempting direct download`); } return await withDownloadLock(version, () => doDownload(version)); } catch (error2) { console.error("Error downloading probe binary:", error2?.message || String(error2)); throw sanitizeError(error2); } } var import_axios, import_fs_extra2, import_path2, import_crypto, import_util, import_child_process, import_tar, import_os2, import_url2, exec, REPO_OWNER, REPO_NAME, BINARY_NAME, __filename2, __dirname2, downloadLocks, LOCK_TIMEOUT_MS, LOCK_POLL_INTERVAL_MS, MAX_LOCK_WAIT_MS; var init_downloader = __esm({ "src/downloader.js"() { "use strict"; import_axios = __toESM(require("axios"), 1); import_fs_extra2 = __toESM(require("fs-extra"), 1); import_path2 = __toESM(require("path"), 1); import_crypto = require("crypto"); import_util = require("util"); import_child_process = require("child_process"); import_tar = __toESM(require("tar"), 1); import_os2 = __toESM(require("os"), 1); import_url2 = require("url"); init_utils(); init_directory_resolver(); exec = (0, import_util.promisify)(import_child_process.exec); REPO_OWNER = "probelabs"; REPO_NAME = "probe"; BINARY_NAME = "probe"; __filename2 = (0, import_url2.fileURLToPath)("file:///"); __dirname2 = import_path2.default.dirname(__filename2); downloadLocks = /* @__PURE__